2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで特定保健指導対象者抽出ツールを作ってみた|Pandasとopenpyxlで健診データを自動判定

2
Last updated at Posted at 2026-06-21

はじめに

こんにちは。

今回は、Pythonを使って「特定保健指導対象者抽出ツール」を作成したので紹介します。

特定保健指導に携わったことがある方なら、

・健診結果から対象者を抽出するのに時間がかかる
・Excelを見ながら一人ひとり判定している
・集計作業が大変
・判定ミスを起こさないか不安

と感じたことがあるのではないでしょうか。

私自身も保健指導業務を行う中で、

「この作業はPythonで自動化できるのでは?」

と思ったことがきっかけで今回のツール作成に挑戦しました。

Python学習を始めてまだ2か月ですが、実際の業務に近い内容をテーマにすることで、学習のモチベーションも高く維持できました。

Python初心者の方や、保健師・医療職の方の参考になれば嬉しいです。

目次

・自己紹介
・作成したツール
・完成イメージ
・判定ロジック
・使用したライブラリ
・Excelデータの準備
・コード全体
・コードの解説
・学べたこと
・作成時に苦労したこと
・今後追加したい機能
・Python初心者だからこそ感じた改善点
・今後の目標
・まとめ

について紹介します。

自己紹介

私は保健師7年目です。

これまで6年以上にわたり、

・特定保健指導
・健診事後フォロー
・健康相談
・保健指導

などに携わってきました。

現在、本業はフリーランスの保健師として活動しています。

一方で、

・業務効率化
・データ活用
・副業スキル

に興味を持ち、Pythonの学習を始めました。

この記事執筆時点では学習開始から約2か月です。

これまでに、

・Pandas
・BeautifulSoup
・スクレイピング
・API取得

などを学習してきました。

今回はそのアウトプットとして、健診データを自動判定するツールを作成しました。

作成したツール

今回作成したツールは、

健診データのExcelファイルを読み込み、自動で支援区分を判定するツールです。

判定結果として、

・積極的支援
・動機付け支援
・情報提供

を自動で分類します。

さらに、

・支援区分ごとの人数集計
・積極的支援対象者の色付け

も行います。

完成イメージ

入力データ
image.png

さらに積極的支援対象者は赤色表示されます。

出力結果

image.png

判定ロジック

今回実装した判定基準は次の通りです。

腹囲基準

男性:85cm以上
女性:90cm以上

BMI基準

BMI25以上

リスク項目

血圧

収縮期血圧130mmHg以上
または拡張期血圧85mmHg以上

血糖

空腹時血糖100mg/dl以上
またはHbA1c 5.2%以上

脂質

中性脂肪150mg/dl以上
またはHDLコレステロール40mg/dl未満

喫煙歴

質問票で「有」

※喫煙歴は血糖・脂質・血圧のいずれかが該当する場合のみ加味しています。

使用したライブラリ

今回使用したライブラリはこちらです。

import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import PatternFill

Pandas

主に

・Excel読み込み
・データ加工
・集計

で使用しました。

openpyxl

主に

・Excel保存
・セルの色付け

で使用しました。

Excelデータの準備

今回使用したExcelファイルは以下のような形式です。

image.png

コード全体

コード全体はこのようになっています。

import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import PatternFill

df=pd.read_excel('health_data.xlsx')

def judge_support(row):

#---------------------------
#リスク
#---------------------------

    risk_count = 0
    if(
        row["収縮期血圧"] >= 130
        or row["拡張期血圧"] >= 85
    ):
        risk_count += 1

    if(
        row["中性脂肪"] >= 150
        or row["HDLコレステロール"]<= 40
    ):
        risk_count += 1

    if(
        row["空腹時血糖"] >= 100
        or row["HbA1c"] > 5.5
    ):
        risk_count += 1

    smoking_risk =(
        risk_count >= 1
        and row["喫煙"] == ""
    )

#---------------------------
#腹囲基準群
#---------------------------
    waist_target =(
        (row["性別"] == "" and row["腹囲"] >= 85)
         or
        (row["性別"] == "" and row["腹囲"] >= 90)
    )

#---------------------------
#BMI基準群
#---------------------------

    BMI_target = row["BMI"] >= 25
    
#---------------------------
#腹囲基準群
#---------------------------

    result = "情報提供"
    
    if waist_target:
        if risk_count >= 2:
            result = "積極的支援"

        elif risk_count == 1 and smoking_risk:
            result = "積極的支援"

        elif risk_count == 1:
            result = "動機付け支援"

        else:
            result = "情報提供"

#---------------------------
#BMI基準群
#---------------------------

    if BMI_target:
        if risk_count >= 3:
            result = "積極的支援"

        elif risk_count == 2 and smoking_risk:
            result = "積極的支援"

        elif risk_count == 2:
            result = "動機付け支援"

        elif risk_count == 1:
            result = "動機付け支援"

        else:
            result = "情報提供"


#---------------------------
#65歳以上は積極的支援の判定数を満たしていても動機付け支援とする。
#---------------------------

    if row["年齢"] >= 65 and result == "積極的支援":
        result = "動機付け支援"

    return result

df["支援区分"]= df.apply(judge_support,axis = 1)
df["リスク数"]= df.apply(count_risk,axis = 1)

df.to_excel("result.xlsx" , index=False)

#---------------------------
#判定処理後のdfを想定
#---------------------------
summary=(
    df["支援区分"]
    .value_counts()
    .reset_index()
)

summary.columns=[
    "支援区分",
    "人数"
]
#---------------------------
#Excels出力
#---------------------------
with pd.ExcelWriter(
    "result.xlsx",
    engine="openpyxl"
) as writer:
    df.to_excel(
        writer,
        sheet_name = "判定結果",
        index = False
    )

    summary.to_excel(
        writer,
        sheet_name = "集計",
        index =False
    )
    
#---------------------------
#色付け
#---------------------------
wb = load_workbook("result.xlsx")

ws = wb["判定結果"]

red_fill = PatternFill(
    fill_type ="solid",
    start_color = "FF0000"
)

headers = {
    cell.value: cell.column
    for cell in ws[1]
}

support_col = headers["支援区分"]
for row in range(
    2,
    ws.max_row +1
):
    cell = ws.cell(
        row = row ,
        column = support_col
    )

    if cell.value == "積極的支援":
        cell.fill = red_fill

wb.save("result.xlsx")

コードの解説

Excelデータを読み込む

まずはPandasを使って健診データのExcelファイルを読み込みます。

import pandas as pd

df = pd.read_excel("health_data.xlsx")

read_excel()を使うことで、ExcelファイルをDataFrameとして扱えます。
DataFrameとは、Excelの表のようなデータ構造です。
今回のツールでは、このDataFrameを使って1人ずつ判定を行います。

判定処理を関数として定義する

次に、支援区分を判定する関数を作成します。

def judge_support(row):

rowには健診受診者1人分のデータが入ります。
例えば、
image.png

のようなデータを1行ずつ取得して判定します。
関数化することで、同じ処理を何度も再利用できるようになります。

リスク数をカウントする

最初にリスク数を保存する変数を準備します。

risk_count = 0

血圧・血糖・脂質の基準に該当するたび、リスク数を1つずつ増やします。

血圧リスクを判定する

血圧は以下の条件です。

収縮期血圧130mmHg以上
または拡張期血圧85mmHg以上

    if(
        row["収縮期血圧"] >= 130
        or row["拡張期血圧"] >= 85
    ):
        risk_count += 1

orを使うことで、どちらか一方に該当した場合、リスクとしてカウントできます。
特定保健指導では、収縮期血圧と拡張期血圧のどちらかが基準値を超えていればリスクとして扱います。

脂質リスクを判定する

脂質は次の条件です。

中性脂肪150mg/dl以上
またはHDLコレステロール40mg/dl未満

if(
        row["中性脂肪"] >= 150
        or row["HDLコレステロール"]<=40
    ):
        risk_count += 1

血圧と同じ考え方で、中性脂肪かHDLコレステロールのどちらかが基準値を超えていればリスクとして扱います。

血糖リスクを判定する

血糖は次の条件で判定します。

空腹時血糖100mg/dl以上
またはHbA1c5.2%以上

if(
        row["空腹時血糖"] >= 100
        or row["HbA1c"] >5.5
    ):
        risk_count += 1

血糖の場合も空腹時血糖かHbA1cのどちらかが基準値を超えていればリスクとして扱います。

喫煙リスクを判定する

今回の判定では、

「血糖・脂質・血圧のいずれかに該当している場合のみ喫煙歴を加味する」

というルールを採用しました。

smoking_risk =(
        risk_count >= 1
        and row["喫煙"] == ""
    )

andを使うことで、

・リスク項目が1つ以上ある
・喫煙歴「有」

の両方を満たした場合だけTrueになります。

腹囲基準を判定する

特定保健指導では、まず腹囲を確認します。

    waist_target =(
        (row["性別"] == "" and row["腹囲"] >= 85)
         or
        (row["性別"] == "" and row["腹囲"] >= 90)
    )

男性と女性で基準が異なるため、条件を分けています。

BMI基準を判定する

腹囲基準に該当しない場合は、BMIを確認します。

BMI_target = row["BMI"] >= 25

BMIが25以上であれば、BMI基準群として判定を行います。

支援区分を判定する

ここが今回のツールの中心部分です。

まず初期値として情報提供を設定します。

result = "情報提供"

その後、

・腹囲基準群
・BMI基準群

に分けて判定します。

例えば腹囲基準群の場合は、

if risk_count >= 2:
            result = "積極的支援"

とすることで、リスク数が2つ以上あれば積極的支援になります。

実際の業務フローをそのままコード化しているイメージです。

65歳以上の判定

特定保健指導では、65歳以上の場合は積極的支援にならず、動機付け支援となります。

そのため最後に年齢判定を行います。

 if row["年齢"] >= 65 and result == "積極的支援":
        result = "動機付け支援"

ここを最後に記述することで、それまでの判定結果を上書きできます。

DataFrame全体に判定を適用する

作成した関数を全受診者に適用します。

df["支援区分"]= df.apply(judge_support,axis = 1)
df["リスク数"]= df.apply(count_risk,axis = 1)

apply()を使うことで、Excelの全行に対して同じ判定処理を実行できます。

結果は新しい「支援区分」と「リスク数」列として追加されます。

Excelへ出力する

判定結果をExcelへ保存します。

df.to_excel("result.xlsx" , index=False)

index=Falseを指定することで、余計な行番号を出力せずに保存できます。

支援区分ごとの人数集計

実際の業務でも、

「今年の積極的支援対象者は何人ですか?」

と聞かれることがあるため、人数集計機能を盛り込んでおります。

・積極的支援は何人いるのか
・動機付け支援は何人いるのか
・情報提供は何人いるのか

を確認できるようにしました。

Pandasにはカテゴリごとの件数を簡単に集計できる機能があります。

今回は value_counts() を使用しました。

人数集計機能で使用したコード

summary=(
    df["支援区分"]
    .value_counts()
    .reset_index()
)

summary.columns=[
    "支援区分",
    "人数"
]

まず、

df["支援区分"]

で支援区分列を取得します。

例えば、
支援区分
・積極的支援
・動機付け支援
・情報提供

というデータがあるとします。

次に、

.value_counts()

を実行すると、

積極的支援 2
動機付け支援 1
情報提供 1

のように自動で件数を集計してくれます。

非常に便利な機能です。

reset_index()を使う理由

value_counts()だけでは表形式になっていません。

そこで、

.reset_index()

を使うことでDataFrame形式に変換します。

結果として、

支援区分 人数
積極的支援 2
動機付け支援 1
情報提供 1

のような見やすい表になります。

集計結果を別シートへ出力

今回は結果を確認しやすいように、

・判定結果シート
・集計シート

の2シート構成にしました。

with pd.ExcelWriter(
    "result.xlsx",
    engine="openpyxl"
) as writer:
    df.to_excel(
        writer,
        sheet_name = "判定結果",
        index = False
    )

    summary.to_excel(
        writer,
        sheet_name = "集計",
        index =False
    )

ExcelWriterとは?

通常の

df.to_excel()

では1シートしか出力できません。

そこで、

pd.ExcelWriter()

を使うことで、

1つのExcelファイルの中に複数シートを作成できます。

積極的支援対象者を赤色表示する

判定結果をExcelで確認していると、

「誰が積極的支援なのか」

ということです。

そこで、

積極的支援対象者だけ赤色表示する機能を入れてみました。

Excelのセルの色を変更するために、

今回はopenpyxlを使用しました。

from openpyxl import load_workbook
from openpyxl.styles import PatternFill

赤色を定義する

まずは塗りつぶし用の色を作成します。

red_fill = PatternFill(
    fill_type ="solid",
    start_color = "FF0000"
)

単色設定

fill_type

fill_type ="solid"

は単色で塗りつぶす設定です。

start_color

start_color = "FF0000"

は赤色を表しています。

Excelでは色を16進数で指定できます。

代表例

コード
FF0000
0000FF
00FF00
FFFF00

Excelファイルを開く

次に出力したExcelを読み込みます。

wb = load_workbook("result.xlsx")

ws = wb["判定結果"]

列番号を自動取得する

最初は

column=13

のように固定で指定していました。

しかし列順が変わると動かなくなります。

そこでヘッダー名から列番号を取得する方法に変更しました。

headers = {
    cell.value: cell.column
    for cell in ws[1]
}

このコードで何をしているのか

1行目(ヘッダー行)を読み取り、

{
 "氏名":1,
 "年齢":2,
 "支援区分":13
}

のような辞書を作成しています。

支援区分列を取得する

support_col = headers["支援区分"]

これで支援区分列の番号が取得できます。

列順が変わっても対応できるため、こちらの方法がおすすめです。

積極的支援だけ色付けする

最後に全行を確認しながら色付けを行います。

for row in range(
    2,
    ws.max_row +1
):
    cell = ws.cell(
        row = row ,
        column = support_col
    )

    if cell.value == "積極的支援":
        cell.fill = red_fill

まず、

range(2, ws.max_row + 1)

で2行目から最終行までループします。

1行目はヘッダーなので除外しています。

次に、

cell.value

でセルの値を取得します。

もし

cell.value == "積極的支援"

なら、

cell.fill = red_fill

を実行してセルを赤く塗りつぶします。

保存する

最後に保存します。

wb.save("result.xlsx")

学べたこと

今回の開発を通して感じたことは、

「Python初心者でも業務改善ツールは作れる」

ということです。

AIや機械学習の知識がなくても、

・PandasによるExcel操作
・if文を使った条件分岐
・実務の判定ロジックをコードに落とし込む考え方

だけで十分実用的なツールを作れます。

特に現場経験がある人ほど、

「こんなツールが欲しかった」

というアイデアを形にしやすいと感じました。

特に印象的だったのは、「Pythonの知識」よりも「業務フローを整理する力」の方が重要だったことです。

現場の知識がある人ほど、Pythonを使った業務改善ツールを作りやすいと感じました。

また

・人数集計
・色付け

を追加したことで、実際の業務でかなり使いやすくなりました。

特に積極的支援対象者が多いデータでは、

Excelを開いた瞬間に対象者を把握できるようになったため、確認作業がとても楽になりました。

また、集計シートを作成したことで、

・対象者数の報告
・月次集計
・年度集計

にも活用できそうです。

Python学習のために作り始めたツールでしたが、実際の業務改善にもつながる良い経験になりました。

作成時に苦労したこと

今回一番難しかったのは、

Pythonを書くことではなく、判定ロジックを整理することでした。

実際の特定保健指導では、

・腹囲基準
・BMI基準
・リスク数
・喫煙歴
・年齢

など複数の条件があります。

頭の中では理解していても、それをコードに落とし込むのは意外と難しかったです。

特に、

・if
・elif
・else

の組み合わせが複雑になり、思った通りに判定されないことが何度もありました。

そのたび、紙に判定フローを書き出して整理しました。

今後追加したい機能

今後はさらに次の機能を追加したいと考えています。

・年代別集計
・BMI平均値算出
・グラフ作成
・PDFレポート出力
・StreamlitによるWebアプリ化
・保健指導コメント自動生成

少しずつ機能を増やしながら、より実務で使えるツールにしていきたいと思います。

Python初心者だからこそ感じた改善点

今回の開発を通して感じたことは、

「まずは動くものを作ることが大切」

ということです。

最初から完璧なシステムを目指していたら、おそらく途中で挫折していたと思います。

まずは、

・Excelを読み込む
・判定する
・Excelへ出力する

という最小限の機能を実装しました。

その結果、

「次は集計もしたい」
「次は色付けしたい」
「次はグラフも作りたい」

という新しいアイデアが自然と出てくるようになりました。

今後の目標

このツールをさらに改善しながら、

最終的には

・健診データ分析
・保健指導対象者抽出
・集計レポート作成
・Webアプリ化

まで実装し、

「保健師の業務を少しでも効率化できるツール」

に成長させたいと考えています。

また、Python学習のアウトプットとして継続的に改善内容をQiitaへ投稿していく予定です。

まとめ

今回はPythonを使って特定保健指導対象者抽出ツールを作成しました。

学習開始から2か月でも、

・Pandas
・openpyxl
・条件分岐

を組み合わせることで、実際の業務に近いツールを作ることができました。

保健師や医療職の方は、現場知識という大きな強みがあります。

Pythonと組み合わせることで、日々の業務を効率化できる可能性が広がります。

この記事が、

・Python学習中の方
・業務効率化に興味がある方
・保健師や医療職の方

の参考になれば嬉しいです。

最後まで読んでいただき、ありがとうございました!

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?