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?

[スニペット] PythonでCSV→Excel & セルに書式設定

Last updated at Posted at 2025-04-25
  1. 必要なライブラリをインストール
pip install pandas xlsxwriter openpyxl
  1. pandas + XlsxWriter で条件付き書式を使う
import pandas as pd

# 1) CSV を読み込む
df = pd.read_csv('input.csv')

# 2) ExcelWriter を使って書き出し(engine='xlsxwriter')
with pd.ExcelWriter('output.xlsx', engine='xlsxwriter') as writer:
    df.to_excel(writer, index=False, sheet_name='Sheet1')
    workbook  = writer.book
    worksheet = writer.sheets['Sheet1']

    # ───────────────
    # 罫線フォーマット
    # ───────────────
    border_fmt = workbook.add_format({
        'border': 1,            # 全辺に細線
    })
    # 列幅を自動調整しつつ、各セルに罫線を適用
    worksheet.set_column('A:Z', None, border_fmt)

    # ───────────────
    # 条件付き書式:A列が 10 より大きい → 赤背景
    # ───────────────
    red_fill = workbook.add_format({
        'bg_color': '#FFC7CE',  # 薄い赤
    })
    # データ部が A2 から A(行数+1) まで
    last_row = len(df) + 1
    worksheet.conditional_format(
        f'A2:A{last_row}',
        {
            'type':     'cell',
            'criteria': '>',
            'value':    10,
            'format':   red_fill
        }
    )

    # ───────────────
    # 別の条件例:B列が文字列 “NG” → 黄色背景
    # ───────────────
    yellow_fill = workbook.add_format({'bg_color': '#FFEB9C'})
    worksheet.conditional_format(
        f'B2:B{last_row}',
        {
            'type':     'text',
            'criteria': 'containing',
            'value':    'NG',
            'format':   yellow_fill
        }
    )

ポイント

  • set_column('A:Z', None, border_fmt) で A〜Z 列まで罫線を設定。必要なら範囲を狭めてください
  • conditional_format なら Excel 側にルールが残るので、あとで手動変更も可能です

  1. openpyxl でセル単位にスタイルを手動で当てる
    XlsxWriter の条件付き書式では難しい、 より細かい独自ルールがある場合はこちらがおすすめです。
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Border, Side

# 1) pandas で読み込んでから openpyxl ワークブックを作成
df = pd.read_csv('input.csv')
wb = Workbook()
ws = wb.active
ws.title = 'Sheet1'

# ヘッダー書き込み
ws.append(list(df.columns))
# データ書き込み
for row in df.itertuples(index=False):
    ws.append(row)

# フォーマット定義
red_fill = PatternFill(fill_type='solid', start_color='FFCCCC', end_color='FFCCCC')
thin_side = Side(border_style="thin", color="000000")
border   = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)

# 2) セルを1つずつ巡回してスタイル設定
for row in ws.iter_rows(min_row=2, max_row=ws.max_row,
                        min_col=1, max_col=ws.max_column):
    for cell in row:
        # 数値が 10 より大きければ赤背景
        if isinstance(cell.value, (int, float)) and cell.value > 10:
            cell.fill = red_fill
        # どんなセルにも罫線を
        cell.border = border

# 3) 保存
wb.save('styled_openpyxl.xlsx')

ポイント

  • PatternFill で好きな色の塗りつぶしが可能
  • Border/Side で罫線スタイルを細かく制御
  • ループ内で if による任意の条件判定ができる

テスト用ダミーデータ

No,都道府県名,最高気温,最低気温,日の出時刻,日没時刻
1,北海道,23.6,12.7,07:00,17:09
2,青森県,31.2,30.4,05:17,18:43
3,岩手県,17.5,11.7,06:17,17:12
4,宮城県,15.4,6.9,05:53,17:56
5,秋田県,19.6,7.5,06:28,17:24
6,山形県,32.9,29.6,05:36,17:25
7,福島県,17.1,10.0,06:30,18:50
8,茨城県,28.8,7.3,06:05,18:22
9,栃木県,19.0,14.3,05:48,17:43
10,群馬県,31.8,13.0,06:23,18:46
11,埼玉県,24.2,13.7,06:49,17:18
12,千葉県,26.7,16.2,06:24,17:55
13,東京都,27.5,24.3,05:40,18:34
14,神奈川県,18.5,9.2,05:11,18:47
15,新潟県,16.1,14.4,05:46,18:04
16,富山県,19.0,17.8,05:26,16:55
17,石川県,34.2,12.3,06:36,17:42
18,福井県,33.0,17.4,05:00,16:41
19,山梨県,24.4,11.6,05:03,18:27
20,長野県,19.5,16.9,05:08,18:17
21,岐阜県,18.6,5.4,06:36,18:42
22,静岡県,19.9,18.2,05:16,17:50
23,愛知県,22.2,19.9,06:33,17:55
24,三重県,21.8,20.3,05:56,17:37
25,滋賀県,17.4,14.5,06:56,16:55
26,京都府,26.5,8.8,05:02,18:24
27,大阪府,24.2,16.9,05:01,17:06
28,兵庫県,26.3,6.7,06:18,16:47
29,奈良県,17.5,11.3,06:27,18:46
30,和歌山県,24.2,12.1,05:57,16:53
31,鳥取県,17.1,6.9,05:44,16:58
32,島根県,16.0,10.5,06:14,18:08
33,岡山県,26.4,16.1,06:03,16:57
34,広島県,20.4,18.8,06:50,17:17
35,山口県,31.1,25.4,06:47,17:22
36,徳島県,26.7,7.2,05:22,17:32
37,香川県,26.7,14.0,05:03,16:55
38,愛媛県,17.0,8.0,05:49,18:12
39,高知県,23.5,13.3,06:22,18:17
40,福岡県,28.2,24.0,06:25,17:12
41,佐賀県,17.4,12.5,06:50,18:58
42,長崎県,15.3,13.4,05:20,17:43
43,熊本県,18.2,8.1,06:27,17:37
44,大分県,30.6,11.8,06:14,17:56
45,宮崎県,31.9,13.5,05:02,18:42
46,鹿児島県,32.8,8.0,06:42,18:09
47,沖縄県,17.7,8.8,05:18,17:23

不定形なCSVの場合

出力条件,,,,,
国,,日本
取得日,,20250425
,
No,都道府県名,最高気温,最低気温,日の出時刻,日没時刻
1,北海道,23.6,12.7,07:00,17:09
2,青森県,31.2,30.4,05:17,18:43
--省略--
import csv
from openpyxl import Workbook
from openpyxl.styles import PatternFill, Border, Side

# 1) CSV を全部読み込む
with open('input.csv', newline='', encoding='utf-8') as f:
    reader = csv.reader(f)
    rows = list(reader)

# 2) Workbook 作成&シート取得
wb = Workbook()
ws = wb.active

# 3) 全行を順に書き出し(1行目~)
for row in rows:
    ws.append(row)

# 4) 書式定義
red_fill = PatternFill(fill_type='solid', start_color='FFC7CE', end_color='FFC7CE')
thin_side = Side(border_style='thin', color='000000')
border = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)

# 5) データ部(6行目=Excel上の row=6 から最終行まで)にだけ書式を適用
max_row = len(rows)
# 5行目(Excel行=5)がヘッダーなので、その下(6行目)から
for row_cells in ws.iter_rows(min_row=6, max_row=max_row,
                              min_col=1, max_col=len(rows[4])):  # rows[4] はヘッダー行の列数
    for cell in row_cells:
        # 罫線
        cell.border = border
        # 数値かどうか試して、10超なら赤背景
        try:
            if float(cell.value) > 10:
                cell.fill = red_fill
        except (TypeError, ValueError):
            pass

# 6) 保存
wb.save('styled_output.xlsx')
# 書式定義
red_fill = PatternFill(fill_type='solid', start_color='FFC7CE', end_color='FFC7CE')
thin = Side(border_style='thin', color='000000')
border = Border(left=thin, right=thin, top=thin, bottom=thin)

# データ部:Excelの6行目から最終行まで(ヘッダーは5行目まで)
for row_cells in ws.iter_rows(min_row=6, max_row=ws.max_row,
                              min_col=1, max_col=ws.max_column):
    for cell in row_cells:
        # まずは全セルに罫線
        cell.border = border

        # 列番号を取得(openpyxl >= 2.6 なら .column で整数が返る)
        col_idx = cell.column

        # 11列目以降だけ処理
        if col_idx > 10:
            # グループ n を計算(0:11~20列, 1:21~30列, …)
            n = (col_idx - 11) // 10
            # グループ内の相対位置 r を計算(1~10)
            #    11列 → r=1, 12列 → r=2, … 20列 → r=10
            r = col_idx - 10 - 10*n

            # r==2 または r>=4 のときだけ色塗り
            if r == 2 or (4 <= r <= 10):
                cell.fill = red_fill
# --- 前提:Workbook, ws, red_fill, border などは既存の定義のまま ---

for row_cells in ws.iter_rows(min_row=6, max_row=ws.max_row,
                              min_col=1, max_col=ws.max_column):
    for cell in row_cells:
        # まずは全セルに罫線
        cell.border = border

        col_idx = cell.column
        # 11列目以降のみ処理
        if col_idx > 10:
            # グループ n を計算(0:11~20列, 1:21~30列, …)
            n = (col_idx - 11) // 10
            # グループ内の相対位置 r を計算(1~10)
            r = col_idx - 10 - 10 * n

            # r==1 のセルを基準値として取得
            # グループ先頭列 = 10 + 10*n + 1
            base_col = 10 + 10 * n + 1
            base_cell = ws.cell(row=cell.row, column=base_col)

            # 値を数値に変換して差分をチェック
            try:
                current = float(cell.value)
                base    = float(base_cell.value)
                if current - base >= 5.5:
                    cell.fill = red_fill
            except (TypeError, ValueError):
                # 非数値セルはスキップ
                pass

色塗りロジック追加
流れのまとめ
①書式定義:pink_fill、blue_fill、green_fill を追加
②ループ内で r == 3 判定

  1. 同じグループの r=4~10 を走査
  2. 差分に応じて 2,1,0,-1,-2 ポイント付与
  3. 合計ポイントに従い、r == 3 セルに塗りつぶし
  4. 既存の r == 2/r >= 4 ロジック はそのまま elif に

これで、r==3 の列にはグループの傾向を色で可視化できるようになります。

from openpyxl.styles import PatternFill, Border, Side

# ────────────
# 書式定義
# ────────────
red_fill   = PatternFill(fill_type='solid', start_color='FFC7CE', end_color='FFC7CE')
pink_fill  = PatternFill(fill_type='solid', start_color='FFC0CB', end_color='FFC0CB')  # ピンク
blue_fill  = PatternFill(fill_type='solid', start_color='ADD8E6', end_color='ADD8E6')  # 青
green_fill = PatternFill(fill_type='solid', start_color='90EE90', end_color='90EE90')  # 緑

thin_side = Side(border_style='thin', color='000000')
border    = Border(left=thin_side, right=thin_side, top=thin_side, bottom=thin_side)

# ────────────
# データ行のループ
# ────────────
for row_cells in ws.iter_rows(min_row=6, max_row=ws.max_row,
                              min_col=1, max_col=ws.max_column):
    for cell in row_cells:
        # ① まずは全セルに罫線
        cell.border = border

        col_idx = cell.column
        if col_idx <= 10:
            continue

        # グループ番号 n と相対位置 r を計算
        n = (col_idx - 11) // 10
        r = col_idx - 10 - 10 * n

        # 基準値セル(r==1)の取得
        base_col  = 10 + 10 * n + 1
        base_cell = ws.cell(row=cell.row, column=base_col)

        # ────────────
        # r==3 のとき:グループ内 r=4~10 のスコアを合計し、その合計で色分け
        # ────────────
        if r == 3:
            total = 0
            for offset in range(4, 11):
                c = ws.cell(row=cell.row, column=10 + 10 * n + offset)
                try:
                    diff = float(c.value) - float(base_cell.value)
                except (TypeError, ValueError):
                    continue
                if diff >= 5.5:
                    pts = 2
                elif diff >= 2.5:
                    pts = 1
                elif diff < -5.5:
                    pts = -2
                elif diff < -2.5:
                    pts = -1
                else:
                    pts = 0
                total += pts

            # 合計ポイントで色分け
            if total > 5:
                cell.fill = red_fill
            elif total > 2:
                cell.fill = pink_fill
            elif total > -2:
                # 塗りつぶしなし
                pass
            elif total > -4:
                cell.fill = blue_fill
            else:
                cell.fill = green_fill

        # ────────────
        # 既存ロジック:r==2 または r>=4 のセル単体の判定
        # ────────────
        elif r == 2 or (4 <= r <= 10):
            try:
                current = float(cell.value)
                base    = float(base_cell.value)
                if current - base >= 5.5:
                    cell.fill = red_fill
            except (TypeError, ValueError):
                pass
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?