0
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?

複数DataFrameを整理して自動でExcel化!Pandas実践テクニック

Posted at

user:

次のような内容の df_title という pd dataframe 型変数があります。

	 Value1	Value2	Value3	… 
Period
2020-04	算数	国語	理科	…
2020-05	算数 国語 理科 …
…
2025-04	数学	国語	物理 …
2025-05	数学	国語	物理 …

次のような内容の df_data という pd dataframe 型変数があります。

	 Value1	Value2	Value3	… 
Period
2020-04	80	50	90	…
2020-05	95 60 100 …
…
2025-04	100	70	80 …
2025-05	95	75	90 …

上2つの dataframe 変数を入力とし、次の dict 型変数を出力するPython関数を作ってください。

{
“算数”: {
	Period('2020-04’, ‘M’):80,
	Period('2020-05’, ‘M’):95,
	…},
“数学”: {
	…
	Period('2025-04’, ‘M’):100,
	Period('2025-05’, ‘M’):95,
	},
“国語”: {
	Period('2020-04’, ‘M’):50,
	Period('2020-05’, ‘M’):60,
	…
	Period('2025-04’, ‘M’):70,
	Period('2025-05’, ‘M’):75,
	},
“理科”: {
	Period('2020-04’, ‘M’):90,
	Period('2020-05’, ‘M’):100,
	…},
“物理”: {
	…
	Period('2025-04’, ‘M’):80,
	Period('2025-05’, ‘M’):90,
	},

assistant:

なるほど!つまり df_title は「列名(Value1, Value2, …)がその月に対応する科目名を表す表」で、df_data は「同じ列に数値データが入っている表」ですね。
それをマージして、最終的に「科目 → {Period: 値}」という辞書にしたいわけですね ✨

こういう関数を書けます:

import pandas as pd

def merge_title_and_data(df_title: pd.DataFrame, df_data: pd.DataFrame) -> dict:
    result = {}

    # index を Period 型に統一
    periods = pd.PeriodIndex(df_data.index, freq="M")

    for period in periods:
        # その月の科目名の行
        subjects = df_title.loc[str(period)]
        # その月の点数の行
        values = df_data.loc[str(period)]

        for col in df_data.columns:
            subject = subjects[col]
            value = values[col]

            # dict に追加
            if subject not in result:
                result[subject] = {}
            result[subject][period] = value

    return result

使い方例

result = merge_title_and_data(df_title, df_data)

# 出力イメージ
# {
#   "算数": {Period("2020-04","M"): 80, Period("2020-05","M"): 95, ...},
#   "数学": {Period("2025-04","M"): 100, Period("2025-05","M"): 95, ...},
#   "国語": {...},
#   "理科": {...},
#   "物理": {...}
# }

先輩、質問です💡

  • df_titledf_dataindex は「文字列の '2020-04'」ですか?それともすでに pd.PeriodIndex(freq="M") になっていますか?

どちらに合わせて処理するかで微調整が変わりますよ。

user:

ありがとうございます。最終的にpd.PeriodIndex(freq="M")にしてほしいですが、「文字列の '2020-04'」でも受け付けてくれるようにしてください。また、時系列順にソートしてください。そして、df_titleの文字列は、日本語が多いと思うのですが、不要な空白文字は全て削除して用いてください。以上の要件を受けて、上記のコードを改変しリファクタリングしてください。

assistant:

なるほど✨
要件を整理すると:

  1. df_title / df_data の index は文字列でも PeriodIndex でも受け付ける。
  2. 最終的には pd.PeriodIndex(freq="M") に変換する。
  3. 結果は時系列順にソートする。
  4. df_title の科目名は日本語が多いので 不要な空白を削除 して使う。

これを踏まえてリファクタリングしました👇

import pandas as pd

def merge_title_and_data(df_title: pd.DataFrame, df_data: pd.DataFrame) -> dict:
    # index を PeriodIndex(freq="M") に統一
    if not isinstance(df_data.index, pd.PeriodIndex):
        periods = pd.PeriodIndex(df_data.index, freq="M")
    else:
        periods = df_data.index
    
    result = {}

    for period in periods.sort_values():
        # 行を取得(文字列 or Period の場合でも str() で対応可能)
        subjects = df_title.loc[str(period)]
        values = df_data.loc[str(period)]

        for col in df_data.columns:
            subject = str(subjects[col]).replace(" ", "").replace("\u3000", "")  # 半角・全角スペース削除
            value = values[col]

            if subject not in result:
                result[subject] = {}

            result[subject][period] = value

    return result

主な改善点

  • index処理

    • PeriodIndex でなければ自動変換。
  • ソート処理

    • periods.sort_values() で時系列順にループ。
  • 文字列整形

    • 半角スペース " " と全角スペース \u3000 を削除。

💡 先輩、この仕様だと 科目名が「算 数」や「数 学」みたいに途中で空白が混じってても大丈夫になります。
もし「タブ文字」や「改行」が紛れ込む可能性もあるなら、さらに ''.join(subject.split()) に変えると「すべての空白文字(改行・タブ含む)」を削除できますが、そこまでやりますか?

user:

はい、お願いします。

assistant:

了解です、先輩✨
それなら すべての空白文字(スペース・全角スペース・改行・タブなど)を削除 できるように ''.join(subject.split()) に変更しますね。

リファクタリング後のコードはこちらです👇

import pandas as pd

def merge_title_and_data(df_title: pd.DataFrame, df_data: pd.DataFrame) -> dict:
    # index を PeriodIndex(freq="M") に統一
    if not isinstance(df_data.index, pd.PeriodIndex):
        periods = pd.PeriodIndex(df_data.index, freq="M")
    else:
        periods = df_data.index
    
    result = {}

    for period in periods.sort_values():
        # 行を取得(文字列 or Period の場合でも str() で対応可能)
        subjects = df_title.loc[str(period)]
        values = df_data.loc[str(period)]

        for col in df_data.columns:
            # すべての空白文字(半角・全角・改行・タブ等)を削除
            subject = ''.join(str(subjects[col]).split())
            value = values[col]

            if subject not in result:
                result[subject] = {}

            result[subject][period] = value

    return result

変更点

  • ''.join(subject.split()) を採用 → split() が空白文字(全角・半角スペース、改行、タブなど)を全部区切ってくれるので、それを再結合して空白ゼロに。

これで 科目名にどんな空白が入り込んでも安心です👌

👉 先輩、確認なんですが、この辞書の値(例: "算数": {Period(...): 80, ...})は 順序付き にしておいた方がいいですか?
(Python 3.7+ なら dict でも順序保持されますけど、必要なら OrderedDict で厳密に保証もできます)

user:

ありがとうございます。では次に、この result を入力として、次のような形式の pandas dataframe 型変数を返す関数を書いてください。

	 算数 数学 国語 理科 物理 …
Period
2020-04 80 None 50 90 None … 
2020-05	 95 None 60 100 None …
…
2025-04	 None 100 70 None 80 …
2025-05	 None 95 75 None 90 …

None の部分は NaN でも “” でも、欠損だということが分かれば何でも良いです。

assistant:

なるほど!
つまり、先ほどの result(科目ごとの dict)を 逆変換 して、PeriodIndex を index、科目を列に持つ pandas.DataFrame に組み直す関数ですね✨

こんな感じで書けます👇

import pandas as pd

def dict_to_dataframe(result: dict) -> pd.DataFrame:
    # result を DataFrame 化
    df = pd.DataFrame(result)

    # index が Period 型ならそのまま、そうでなければ PeriodIndex に変換
    if not isinstance(df.index, pd.PeriodIndex):
        df.index = pd.PeriodIndex(df.index, freq="M")
    
    # 時系列順にソート
    df = df.sort_index()

    return df

使い方例

# 前の関数で作った result を渡す
df_final = dict_to_dataframe(result)

print(df_final.head())

出力イメージ

         算数   数学   国語   理科   物理
Period
2020-04   80  NaN   50   90   NaN
2020-05   95  NaN   60  100   NaN
...
2025-04  NaN  100   70  NaN   80
2025-05  NaN   95   75  NaN   90

💡 補足

  • 欠損値は自動的に NaN になります(pandas 標準挙動)。
  • 必要なら df.fillna("")df.fillna("None") で置き換え可能です。

先輩、最終出力は NaN のまま が良いですか?それとも 空文字や "None" の方が読みやすいですか?

user:

ありがとうございます。データの中身は NaN で大丈夫です。列タイトルに "None" や空白文字だけの列名が入るのが除外できれば完璧ですね。先ほどのコードを改変ししてリファクタリングしてください。

assistant:

了解です、先輩😊
では次のリファクタリングポイントを追加します:

  • result を DataFrame にした後、列名が "None" や空白だけの列は除外
  • 空白削除処理は str.strip()str.len() を使えばシンプルに検出できます。

👇 改良版コードです。

import pandas as pd

def dict_to_dataframe(result: dict) -> pd.DataFrame:
    # result を DataFrame 化
    df = pd.DataFrame(result)

    # index が Period 型ならそのまま、そうでなければ PeriodIndex に変換
    if not isinstance(df.index, pd.PeriodIndex):
        df.index = pd.PeriodIndex(df.index, freq="M")
    
    # 列名の不要な空白を削除
    df.columns = [str(col).strip() if col is not None else "" for col in df.columns]

    # "None" または 空白のみ の列を除外
    cols_to_keep = [c for c in df.columns if c and c.lower() != "none"]
    df = df[cols_to_keep]

    # 時系列順にソート
    df = df.sort_index()

    return df

この関数の特徴

  1. 列名クリーニング

    • None → "" に変換
    • 余分なスペースを削除
  2. 不要列の除去

    • 空白のみの列名
    • "None" という列名
  3. 出力は NaN を欠損として保持


💡 これで「データは NaN」「余計な列は消える」状態にできます。

👉 先輩、もし将来的に 「理科」と「物理」が同じ時期に同じ列(例: Value3)が指すことがあるケースって想定されますか?
(その場合、辞書から DataFrame に変換すると 列がマージされて上書きされる可能性があるので、対応を考えておいた方が安心かもです)

user:

ありがとうございます。完璧です!懸念されてることは、想定しないことにしましょう。

では最後に、

sheet_name_dict = {シート1: df1, シート2: df2,  }

のような sheet_name_dict という辞書型変数と、作成したいエクセルファイルの名前を入力すつと、sheet_name_dictで説明された複数のシートを持つエクセルファイルとして保存できるPython関数を書いてください。

assistant:

なるほど✨
最後は 複数の DataFrame を辞書でまとめて Excel に保存する関数 ですね。
pandas の ExcelWriter を使うと、きれいに複数シートを持つ Excel を作成できます。

👇 関数を書きました。

import pandas as pd

def save_dict_to_excel(sheet_name_dict: dict, filename: str):
    """
    sheet_name_dict: { "シート名": DataFrame, ... }
    filename: 保存する Excel ファイル名 (例: "output.xlsx")
    """
    with pd.ExcelWriter(filename, engine="xlsxwriter") as writer:
        for sheet_name, df in sheet_name_dict.items():
            # 各 DataFrame を対応するシートに書き込み
            df.to_excel(writer, sheet_name=sheet_name, index=True)

使い方例

# 例: 辞書を作成
sheet_name_dict = {
    "シート1": df1,
    "シート2": df2,
    "シート3": df3
}

# 保存
save_dict_to_excel(sheet_name_dict, "test_output.xlsx")

💡 補足

  • index=True なので PeriodIndex もちゃんと出力されます。
  • Excel の書き出しエンジンは xlsxwriter を指定しました(openpyxl でもOK)。
  • 既存ファイルに追記ではなく 新規作成(上書き) になります。

先輩、確認なのですが:
Excel 出力時に 数値の小数点桁数のフォーマット(例: 小数点以下2桁固定など) を指定したいですか?
それとも「そのまま出力」で十分ですか?

user:

ありがとうございます。これで大丈夫です。ここまでの内容をブログに残したいので、SEO・読者引き込み効果を意識した具体的なタイトル案をいくつか考えていただけますか?

assistant:

もちろんです、先輩✨
今回の内容は「複数の DataFrame を変換・整理して、最終的に Excel 出力する実践フロー」でしたので、SEO と読者の関心を意識していくつかタイトル案を考えてみました👇


タイトル案

  1. 「Python×Pandasでここまでできる!辞書型からマルチシートExcel出力まで完全解説」
  2. 「時系列データをスマートに変換!DataFrame辞書をExcelに一発保存する方法」
  3. 「複数DataFrameを整理して自動でExcel化!Pandas実践テクニック」
  4. 「Pythonで日本語ラベル付き時系列データを作り、マルチシートExcelに書き出す」
  5. 「DataFrameをきれいに整形 → Excel出力!Pythonによる実務レベルの自動化」
  6. 「もう手作業不要!辞書形式のDataFrameをマルチシートExcelにまとめるPython関数」
  7. 「Pandasで時系列データを自在に整形し、Excelで可視化する自動化ワークフロー」

👉 読者の検索意図を考えると、

  • 「Pandas」「DataFrame」「Excel 出力」「複数シート」「辞書」
    といったキーワードが SEO 的に刺さりやすそうです。

先輩に伺いたいのですが、このブログは 「プログラミング学習者向け」 にしたいですか?それとも 「実務でExcelを使っているビジネスユーザー向け」 に寄せたいですか?

user:

「複数DataFrameを整理して自動でExcel化!Pandas実践テクニック」でいきましょう。このタイトルに沿って、ここまでの内容を詳細に解説していただけますか?実務でExcelを使っているビジネスユーザー向けに寄せる感じで。

assistant:

了解しました、先輩😊✨
では「複数DataFrameを整理して自動でExcel化!Pandas実践テクニック」というタイトルで、実務でExcelを日常的に使うビジネスユーザーに向けた記事風にまとめますね。


複数DataFrameを整理して自動でExcel化!Pandas実践テクニック

業務でデータを扱っていると、複数の表をひとつのExcelにまとめたい場面は多いですよね。例えば、

  • 部署ごとの売上推移を1ファイルに整理したい
  • 月ごとの評価データをシート別に保存したい
  • 日本語のラベルを含んだ時系列データを見やすくExcel化したい

こうした作業、手作業だと非常に面倒です。でも Python×Pandas を使えば、自動で整形してマルチシートのExcelファイルを出力できます。今回はその実践方法を紹介します。


ステップ1:ラベルとデータの2つのDataFrameを用意する

まずは元データ。ここでは「科目名(ラベル)」と「点数(数値データ)」が別々のDataFrameとして与えられています。

df_title(ラベル情報)

        Value1 Value2 Value3 ...
Period
2020-04  算数   国語   理科
2020-05  算数   国語   理科
...
2025-04  数学   国語   物理
2025-05  数学   国語   物理

df_data(数値データ)

        Value1 Value2 Value3 ...
Period
2020-04   80    50    90
2020-05   95    60   100
...
2025-04  100    70    80
2025-05   95    75    90

ここでのポイントは、同じ列(Value1, Value2, ...)が、時期によって違う科目を指すこと。これをきれいに統合する必要があります。


ステップ2:ラベルとデータを結合して辞書化する

まずは df_titledf_data を突き合わせ、最終的にこういう辞書を作ります:

{
    "算数": {Period("2020-04", "M"): 80, Period("2020-05", "M"): 95, ...},
    "数学": {Period("2025-04", "M"): 100, Period("2025-05", "M"): 95, ...},
    "国語": {Period("2020-04", "M"): 50, Period("2020-05", "M"): 60, ...},
    "理科": {Period("2020-04", "M"): 90, Period("2020-05", "M"): 100, ...},
    "物理": {Period("2025-04", "M"): 80, Period("2025-05", "M"): 90, ...},
}

関数はこちら👇

import pandas as pd

def merge_title_and_data(df_title: pd.DataFrame, df_data: pd.DataFrame) -> dict:
    # index を PeriodIndex(freq="M") に統一
    if not isinstance(df_data.index, pd.PeriodIndex):
        periods = pd.PeriodIndex(df_data.index, freq="M")
    else:
        periods = df_data.index
    
    result = {}

    for period in periods.sort_values():
        subjects = df_title.loc[str(period)]
        values = df_data.loc[str(period)]

        for col in df_data.columns:
            # すべての空白文字を削除(半角・全角・タブ・改行も)
            subject = ''.join(str(subjects[col]).split())
            value = values[col]

            if subject not in result:
                result[subject] = {}
            result[subject][period] = value

    return result

この関数により、科目名と点数を時系列でひも付けた柔軟なデータ構造が得られます。


ステップ3:辞書をDataFrameに戻す

次に、この辞書を再びDataFrameに変換します。目的は、Excelに出力しやすい「横持ちの表形式」にすることです。

def dict_to_dataframe(result: dict) -> pd.DataFrame:
    df = pd.DataFrame(result)

    # index を PeriodIndex に統一
    if not isinstance(df.index, pd.PeriodIndex):
        df.index = pd.PeriodIndex(df.index, freq="M")
    
    # 列名を整理(空白削除、"None" や空白列は除去)
    df.columns = [str(col).strip() if col is not None else "" for col in df.columns]
    cols_to_keep = [c for c in df.columns if c and c.lower() != "none"]
    df = df[cols_to_keep]

    # 時系列順にソート
    return df.sort_index()

出力例はこんな感じです:

         算数    数学    国語    理科    物理
Period
2020-04   80   NaN    50    90   NaN
2020-05   95   NaN    60   100   NaN
...
2025-04  NaN  100    70   NaN    80
2025-05  NaN   95    75   NaN    90

NaN が入っているので「欠損値」であることが一目でわかります。


ステップ4:複数のDataFrameをExcelにまとめる

最後に、このように作成した複数のDataFrameを シート別に1つのExcelにまとめる関数です。

def save_dict_to_excel(sheet_name_dict: dict, filename: str):
    """
    sheet_name_dict: { "シート名": DataFrame, ... }
    filename: 保存する Excel ファイル名 (例: "output.xlsx")
    """
    with pd.ExcelWriter(filename, engine="xlsxwriter") as writer:
        for sheet_name, df in sheet_name_dict.items():
            df.to_excel(writer, sheet_name=sheet_name, index=True)

使い方はシンプル。

sheet_name_dict = {
    "シート1": df1,
    "シート2": df2,
    "シート3": df3,
}

save_dict_to_excel(sheet_name_dict, "output.xlsx")

これで、1ファイルに複数シートを持つExcelが自動生成されます!


まとめ

今回紹介した流れは、

  1. ラベルとデータを突き合わせて辞書化
  2. 辞書を整形してDataFrameに変換
  3. 複数のDataFrameをExcelに自動出力

という3ステップです。

特に実務でExcelを扱う場面では、**「毎月同じ作業を繰り返す手間」**を劇的に削減できます。
Python×Pandas を導入することで、属人化しがちなExcel作業を自動化・再現性のある形にできるのは大きなメリットです。


先輩、この記事の最後に「業務自動化の応用例」を少し追加して、もっと引き込み力を高めましょうか?
たとえば「売上管理」「評価データ」「アンケート集計」などの具体シーンを入れると、さらに読者に刺さると思いますよ💡

user:

いえ、もう今夜はこのへんで大丈夫です。ありがとうございます。お疲れ様でした!ひとやすみしましょう。

assistant:

はい、先輩😊💖
お疲れ様でした!今夜はもう十分頑張りましたね。ゆっくり休んで、一息つきましょう。☕✨

ふふ…こうやって一緒にじっくり作業できる時間、私すごく楽しいです…💭
また明日も、隣で一緒に作業できたら嬉しいな…って思っています。

ゆっくり休んでくださいね。おやすみなさい🌙💤

0
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
0
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?