Python自動化マスター第5編:ExcelとCSVファイルの自動化
Python Automation Master Part 5: Excel and CSV File Automation
はじめに:スプレッドシート自動化の重要性
ExcelとCSVファイルは、ビジネス環境で最も広く使用されているデータ形式です。毎日膨大なデータがスプレッドシート形式で生成、処理、報告されています。このような繰り返し作業をPythonで自動化すれば、時間を節約し、人為的ミスを減らすことができます。
今回の第5編では、Pythonのcsvモジュール、pandas、openpyxlライブラリを活用してCSVとExcelファイルを効率的に処理する方法を学びます。基本的なファイルの読み書きからセルスタイリング、チャート作成、大容量ファイル処理まで、実務ですぐに活用できる技術を取り上げます。
1. CSVファイル処理の基礎(csvモジュール)
Pythonの組み込みcsvモジュールは、追加インストールなしでCSVファイルを処理できます。
1.1 CSVファイルの読み込み
import csv
# 基本的なCSV読み込み
with open('data.csv', 'r', encoding='utf-8') as file:
reader = csv.reader(file)
for row in reader:
print(row) # 各行はリストとして返される
# 辞書として読み込み(ヘッダーベース)
with open('data.csv', 'r', encoding='utf-8') as file:
reader = csv.DictReader(file)
for row in reader:
print(row['名前'], row['年齢']) # 列名でアクセス
# 区切り文字の指定
with open('data.tsv', 'r', encoding='utf-8') as file:
reader = csv.reader(file, delimiter='\t') # タブ区切り
for row in reader:
print(row)
1.2 CSVファイルの書き込み
import csv
# 基本的なCSV書き込み
data = [
['名前', '年齢', '都市'],
['田中太郎', 25, '東京'],
['山田花子', 30, '大阪'],
['佐藤次郎', 28, '名古屋']
]
with open('output.csv', 'w', encoding='utf-8-sig', newline='') as file:
writer = csv.writer(file)
writer.writerows(data) # 複数行を一度に書き込み
# 辞書として書き込み
data_dict = [
{'名前': '田中太郎', '年齢': 25, '都市': '東京'},
{'名前': '山田花子', '年齢': 30, '都市': '大阪'},
{'名前': '佐藤次郎', '年齢': 28, '都市': '名古屋'}
]
with open('output_dict.csv', 'w', encoding='utf-8-sig', newline='') as file:
fieldnames = ['名前', '年齢', '都市']
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader() # ヘッダーを書き込み
writer.writerows(data_dict)
Tip: WindowsでExcelでCSVファイルを開く際に日本語が文字化けするのを防ぐには、encoding='utf-8-sig'を使用してください。BOM(Byte Order Mark)が追加され、Excelで正しく認識されます。
2. pandasでCSVを扱う
pandasはデータ分析に最適化されたライブラリで、CSVファイル処理に非常に強力な機能を提供します。
2.1 pandasのインストールと基本使用法
# インストール
pip install pandas
import pandas as pd
# CSV読み込み
df = pd.read_csv('data.csv', encoding='utf-8')
# 基本情報の確認
print(df.head()) # 最初の5行
print(df.info()) # データ型と欠損値情報
print(df.describe()) # 統計サマリー
# 特定の列を選択
print(df['名前']) # 単一列
print(df[['名前', '年齢']]) # 複数列
# 条件フィルタリング
adults = df[df['年齢'] >= 20]
tokyo_people = df[df['都市'] == '東京']
2.2 データの前処理と加工
import pandas as pd
df = pd.read_csv('sales_data.csv')
# 欠損値の処理
df.fillna(0, inplace=True) # 0で埋める
df.dropna(subset=['必須列'], inplace=True) # 特定列の欠損値行を削除
# 新しい列を追加
df['合計'] = df['単価'] * df['数量']
df['割引価格'] = df['合計'] * 0.9
# 列名の変更
df.rename(columns={'old_name': 'new_name'}, inplace=True)
# データのソート
df.sort_values(by='合計', ascending=False, inplace=True)
# グループ別集計
summary = df.groupby('カテゴリ').agg({
'合計': 'sum',
'数量': 'mean',
'注文番号': 'count'
}).rename(columns={'注文番号': '注文数'})
# CSVとして保存
df.to_csv('processed_data.csv', index=False, encoding='utf-8-sig')
2.3 複数のCSVファイルの結合
import pandas as pd
import glob
# 同じ形式の複数CSVファイルを結合
csv_files = glob.glob('data/*.csv')
df_list = [pd.read_csv(f) for f in csv_files]
combined_df = pd.concat(df_list, ignore_index=True)
# 2つのDataFrameを結合(JOIN)
df1 = pd.read_csv('customers.csv')
df2 = pd.read_csv('orders.csv')
# 内部結合(両方にあるもののみ)
merged = pd.merge(df1, df2, on='顧客ID', how='inner')
# 外部結合(すべてのデータを含む)
merged_all = pd.merge(df1, df2, on='顧客ID', how='outer')
3. openpyxlでExcelファイルの作成/編集
openpyxlはExcelファイル(.xlsx)の作成と編集に特化したライブラリです。
3.1 インストールと基本使用法
# インストール
pip install openpyxl
from openpyxl import Workbook, load_workbook
# 新規ワークブックの作成
wb = Workbook()
ws = wb.active
ws.title = "売上データ"
# データ入力
ws['A1'] = '製品名'
ws['B1'] = '数量'
ws['C1'] = '単価'
# 複数行を追加
data = [
['ノートPC', 10, 120000],
['マウス', 50, 2500],
['キーボード', 30, 4500]
]
for row in data:
ws.append(row)
# 保存
wb.save('sales.xlsx')
# 既存ファイルを開く
wb = load_workbook('sales.xlsx')
ws = wb.active
# セル値の読み取り
print(ws['A1'].value)
print(ws.cell(row=2, column=1).value)
# 範囲の読み取り
for row in ws.iter_rows(min_row=2, max_row=4, min_col=1, max_col=3):
for cell in row:
print(cell.value, end=' ')
print()
3.2 セルスタイリング(フォント、色、罫線)
from openpyxl import Workbook
from openpyxl.styles import Font, Fill, PatternFill, Border, Side, Alignment
wb = Workbook()
ws = wb.active
# データ入力
headers = ['製品名', '数量', '単価', '合計']
for col, header in enumerate(headers, 1):
ws.cell(row=1, column=col, value=header)
# フォントスタイル
bold_font = Font(bold=True, size=12, color='FFFFFF')
normal_font = Font(size=11)
# 背景色
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
alt_fill = PatternFill(start_color='D9E2F3', end_color='D9E2F3', fill_type='solid')
# 罫線
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 配置
center_align = Alignment(horizontal='center', vertical='center')
right_align = Alignment(horizontal='right')
# ヘッダーにスタイルを適用
for col in range(1, 5):
cell = ws.cell(row=1, column=col)
cell.font = bold_font
cell.fill = header_fill
cell.border = thin_border
cell.alignment = center_align
# データ行のスタイル
data = [
['ノートPC', 10, 120000],
['マウス', 50, 2500],
['キーボード', 30, 4500]
]
for row_idx, row_data in enumerate(data, 2):
for col_idx, value in enumerate(row_data, 1):
cell = ws.cell(row=row_idx, column=col_idx, value=value)
cell.border = thin_border
if row_idx % 2 == 0:
cell.fill = alt_fill
# 列幅の調整
ws.column_dimensions['A'].width = 15
ws.column_dimensions['B'].width = 10
ws.column_dimensions['C'].width = 15
ws.column_dimensions['D'].width = 15
# 行の高さの調整
ws.row_dimensions[1].height = 25
wb.save('styled_sales.xlsx')
3.3 数式の挿入
from openpyxl import Workbook
wb = Workbook()
ws = wb.active
# データ入力
ws['A1'] = '数量'
ws['B1'] = '単価'
ws['C1'] = '合計'
data = [
[10, 120000],
[50, 2500],
[30, 4500]
]
for row_idx, (qty, price) in enumerate(data, 2):
ws.cell(row=row_idx, column=1, value=qty)
ws.cell(row=row_idx, column=2, value=price)
# 数式を挿入(合計 = 数量 * 単価)
ws.cell(row=row_idx, column=3, value=f'=A{row_idx}*B{row_idx}')
# 合計数式
last_row = len(data) + 1
ws.cell(row=last_row + 1, column=1, value='合計')
ws.cell(row=last_row + 1, column=3, value=f'=SUM(C2:C{last_row})')
# 条件付き数式
ws['D1'] = 'ランク'
for row_idx in range(2, last_row + 1):
ws.cell(row=row_idx, column=4,
value=f'=IF(C{row_idx}>=10000000,"A",IF(C{row_idx}>=1000000,"B","C"))')
wb.save('formulas.xlsx')
4. チャートの作成
openpyxlを使用すると、Excelで様々なチャートをプログラム的に作成できます。
from openpyxl import Workbook
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
wb = Workbook()
ws = wb.active
# データ入力
data = [
['月', '売上', '費用', '利益'],
['1月', 500000, 300000, 200000],
['2月', 600000, 350000, 250000],
['3月', 550000, 320000, 230000],
['4月', 700000, 400000, 300000],
['5月', 800000, 450000, 350000],
['6月', 750000, 420000, 330000]
]
for row in data:
ws.append(row)
# 棒グラフ
bar_chart = BarChart()
bar_chart.type = "col" # 縦棒
bar_chart.style = 10
bar_chart.title = "月別売上状況"
bar_chart.y_axis.title = "金額(円)"
bar_chart.x_axis.title = "月"
# データ範囲の指定
data_ref = Reference(ws, min_col=2, min_row=1, max_col=4, max_row=7)
categories = Reference(ws, min_col=1, min_row=2, max_row=7)
bar_chart.add_data(data_ref, titles_from_data=True)
bar_chart.set_categories(categories)
bar_chart.shape = 4 # チャートスタイル
ws.add_chart(bar_chart, "F2") # チャートの位置
# 折れ線グラフ
line_chart = LineChart()
line_chart.title = "月別推移"
line_chart.style = 13
line_chart.y_axis.title = "金額"
line_chart.x_axis.title = "月"
line_chart.add_data(data_ref, titles_from_data=True)
line_chart.set_categories(categories)
ws.add_chart(line_chart, "F18")
# 円グラフ(6月のデータのみ)
pie_chart = PieChart()
pie_chart.title = "6月費用構成"
labels = Reference(ws, min_col=2, max_col=4, min_row=1)
pie_data = Reference(ws, min_col=2, max_col=4, min_row=7)
pie_chart.add_data(pie_data, titles_from_data=False)
pie_chart.set_categories(labels)
ws.add_chart(pie_chart, "N2")
wb.save('charts.xlsx')
5. 複数シートの操作
from openpyxl import Workbook, load_workbook
# 新規ワークブックに複数シートを作成
wb = Workbook()
# 最初のシート(デフォルトシート)
ws1 = wb.active
ws1.title = "サマリー"
# 追加シートの作成
ws2 = wb.create_sheet("第1四半期")
ws3 = wb.create_sheet("第2四半期")
ws4 = wb.create_sheet("第3四半期", 0) # 先頭に挿入
# シート別にデータを入力
quarters = {
'第1四半期': [['1月', 100], ['2月', 120], ['3月', 110]],
'第2四半期': [['4月', 130], ['5月', 140], ['6月', 135]],
'第3四半期': [['7月', 150], ['8月', 160], ['9月', 155]]
}
for sheet_name, data in quarters.items():
ws = wb[sheet_name]
ws.append(['月', '売上'])
for row in data:
ws.append(row)
# サマリーシートに各四半期の合計を参照
summary = wb['サマリー']
summary.append(['四半期', '総売上'])
summary.append(['第1四半期', '=SUM(\'第1四半期\'!B2:B4)'])
summary.append(['第2四半期', '=SUM(\'第2四半期\'!B2:B4)'])
summary.append(['第3四半期', '=SUM(\'第3四半期\'!B2:B4)'])
# シートの順序を変更
wb._sheets = [wb['サマリー'], wb['第1四半期'], wb['第2四半期'], wb['第3四半期']]
# シートのコピー
source = wb['第1四半期']
target = wb.copy_worksheet(source)
target.title = "第1四半期_バックアップ"
# シートの削除
# del wb['第1四半期_バックアップ']
wb.save('multi_sheets.xlsx')
# 既存ファイルのすべてのシートを巡回
wb = load_workbook('multi_sheets.xlsx')
for sheet_name in wb.sheetnames:
ws = wb[sheet_name]
print(f"シート: {sheet_name}")
for row in ws.iter_rows(values_only=True):
print(row)
6. Excelファイルの結合
import pandas as pd
from openpyxl import load_workbook
import glob
# pandasを使用したExcelファイルの結合
def merge_excel_files(file_pattern, output_file):
"""複数のExcelファイルを1つに結合"""
excel_files = glob.glob(file_pattern)
df_list = []
for file in excel_files:
df = pd.read_excel(file)
df['元ファイル'] = file # 出典を表示
df_list.append(df)
combined_df = pd.concat(df_list, ignore_index=True)
combined_df.to_excel(output_file, index=False)
print(f"結合完了: {len(excel_files)}ファイル -> {output_file}")
merge_excel_files('reports/*.xlsx', 'combined_report.xlsx')
# 複数シートを1つのシートに結合
def merge_sheets(input_file, output_file):
"""1つのExcelファイル内のすべてのシートを結合"""
xl = pd.ExcelFile(input_file)
df_list = []
for sheet_name in xl.sheet_names:
df = pd.read_excel(xl, sheet_name=sheet_name)
df['シート名'] = sheet_name
df_list.append(df)
combined_df = pd.concat(df_list, ignore_index=True)
combined_df.to_excel(output_file, index=False)
merge_sheets('multi_sheets.xlsx', 'merged_single_sheet.xlsx')
# 条件付き結合(特定の列を基準に)
def conditional_merge(files, key_column, output_file):
"""特定のキー列を基準に複数ファイルを結合"""
base_df = pd.read_excel(files[0])
for file in files[1:]:
df = pd.read_excel(file)
base_df = pd.merge(base_df, df, on=key_column, how='outer')
base_df.to_excel(output_file, index=False)
conditional_merge(['customers.xlsx', 'orders.xlsx', 'payments.xlsx'],
'顧客ID', 'customer_full_data.xlsx')
7. 大容量ファイルの処理(チャンク処理)
数十万行以上の大容量ファイルを処理する際は、メモリ効率を考慮する必要があります。
import pandas as pd
from openpyxl import load_workbook
from openpyxl.utils.dataframe import dataframe_to_rows
# チャンク単位で大容量CSVを処理
def process_large_csv(input_file, output_file, chunk_size=10000):
"""大容量CSVをチャンク単位で処理"""
processed_chunks = []
for chunk in pd.read_csv(input_file, chunksize=chunk_size):
# 各チャンクの処理
chunk['処理済み'] = True
chunk['合計'] = chunk['数量'] * chunk['単価']
processed_chunks.append(chunk)
print(f"処理された行: {len(chunk)}")
# 結果を保存
result_df = pd.concat(processed_chunks, ignore_index=True)
result_df.to_csv(output_file, index=False)
# チャンクごとにフィルタリングして保存(メモリ節約)
def filter_large_csv(input_file, output_file, condition_func, chunk_size=10000):
"""大容量CSVから条件に合う行のみを抽出"""
first_chunk = True
for chunk in pd.read_csv(input_file, chunksize=chunk_size):
filtered = chunk[condition_func(chunk)]
if first_chunk:
filtered.to_csv(output_file, index=False, mode='w')
first_chunk = False
else:
filtered.to_csv(output_file, index=False, mode='a', header=False)
# 使用例:合計が100万円以上の行のみを抽出
filter_large_csv(
'large_sales.csv',
'high_value_sales.csv',
lambda df: df['合計'] >= 1000000
)
# 大容量Excelファイルの読み込み(read_onlyモード)
def read_large_excel(file_path):
"""大容量Excelファイルをメモリ効率的に読み込み"""
wb = load_workbook(file_path, read_only=True)
ws = wb.active
for row in ws.iter_rows(values_only=True):
yield row # ジェネレータで1行ずつ返す
wb.close()
# 大容量Excelファイルの書き込み(write_onlyモード)
from openpyxl import Workbook
def write_large_excel(data_generator, output_file):
"""大容量データをExcelに効率的に書き込み"""
wb = Workbook(write_only=True)
ws = wb.create_sheet()
for row in data_generator:
ws.append(row)
wb.save(output_file)
8. 実践:月次レポート自動生成
これまで学んだ内容を総合して、実務で使用できる月次販売レポート自動生成システムを作成してみましょう。
import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side, Alignment
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
from openpyxl.utils.dataframe import dataframe_to_rows
from datetime import datetime
import os
class MonthlyReportGenerator:
"""月次販売レポート自動生成クラス"""
def __init__(self, data_file, output_dir='reports'):
self.data_file = data_file
self.output_dir = output_dir
self.df = None
self.wb = None
# 出力ディレクトリの作成
os.makedirs(output_dir, exist_ok=True)
# スタイル定義
self.header_font = Font(bold=True, size=11, color='FFFFFF')
self.header_fill = PatternFill(start_color='2F5496', end_color='2F5496', fill_type='solid')
self.border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
self.center_align = Alignment(horizontal='center', vertical='center')
self.number_format = '#,##0'
self.currency_format = '#,##0"円"'
def load_data(self):
"""データのロード"""
self.df = pd.read_csv(self.data_file, parse_dates=['注文日'])
self.df['月'] = self.df['注文日'].dt.to_period('M')
print(f"データロード完了: {len(self.df)}行")
def create_summary_sheet(self, ws):
"""サマリーシートの作成"""
ws.title = "サマリー"
# タイトル
ws['A1'] = '月次販売レポート'
ws['A1'].font = Font(bold=True, size=16)
ws.merge_cells('A1:E1')
# 作成日時
ws['A2'] = f'作成日時: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}'
ws['A2'].font = Font(italic=True, size=10)
# 月別サマリーデータ
monthly_summary = self.df.groupby('月').agg({
'注文番号': 'count',
'数量': 'sum',
'合計': 'sum'
}).reset_index()
monthly_summary.columns = ['月', '注文件数', '販売数量', '総売上']
# テーブル開始位置
start_row = 4
headers = ['月', '注文件数', '販売数量', '総売上']
# ヘッダー作成
for col, header in enumerate(headers, 1):
cell = ws.cell(row=start_row, column=col, value=header)
cell.font = self.header_font
cell.fill = self.header_fill
cell.border = self.border
cell.alignment = self.center_align
# データ作成
for row_idx, row in monthly_summary.iterrows():
ws.cell(row=start_row + row_idx + 1, column=1, value=str(row['月']))
ws.cell(row=start_row + row_idx + 1, column=2, value=row['注文件数'])
ws.cell(row=start_row + row_idx + 1, column=3, value=row['販売数量'])
cell = ws.cell(row=start_row + row_idx + 1, column=4, value=row['総売上'])
cell.number_format = self.currency_format
for col in range(1, 5):
ws.cell(row=start_row + row_idx + 1, column=col).border = self.border
# 合計行
total_row = start_row + len(monthly_summary) + 1
ws.cell(row=total_row, column=1, value='合計').font = Font(bold=True)
ws.cell(row=total_row, column=2, value=f'=SUM(B{start_row+1}:B{total_row-1})')
ws.cell(row=total_row, column=3, value=f'=SUM(C{start_row+1}:C{total_row-1})')
ws.cell(row=total_row, column=4, value=f'=SUM(D{start_row+1}:D{total_row-1})')
ws.cell(row=total_row, column=4).number_format = self.currency_format
for col in range(1, 5):
ws.cell(row=total_row, column=col).border = self.border
# 列幅の調整
ws.column_dimensions['A'].width = 12
ws.column_dimensions['B'].width = 12
ws.column_dimensions['C'].width = 12
ws.column_dimensions['D'].width = 18
# チャートの追加
chart = BarChart()
chart.type = "col"
chart.style = 10
chart.title = "月別売上状況"
chart.y_axis.title = "売上(円)"
data = Reference(ws, min_col=4, min_row=start_row,
max_row=start_row + len(monthly_summary))
cats = Reference(ws, min_col=1, min_row=start_row + 1,
max_row=start_row + len(monthly_summary))
chart.add_data(data, titles_from_data=True)
chart.set_categories(cats)
chart.shape = 4
ws.add_chart(chart, "F4")
return monthly_summary
def generate_report(self):
"""レポート生成メイン関数"""
print("レポート生成開始...")
# データロード
self.load_data()
# ワークブック作成
self.wb = Workbook()
# シート作成
ws_summary = self.wb.active
self.create_summary_sheet(ws_summary)
# ファイル保存
today = datetime.now().strftime('%Y%m')
output_file = os.path.join(self.output_dir, f'月次レポート_{today}.xlsx')
self.wb.save(output_file)
print(f"レポート生成完了: {output_file}")
return output_file
# 使用例
if __name__ == "__main__":
generator = MonthlyReportGenerator('sample_sales_data.csv')
generator.generate_report()
まとめ
今回の編では、Pythonを活用したExcelとCSVファイル自動化の核心技術を学びました。
- csvモジュール:シンプルなCSVファイルの読み書きに適しています
- pandas:データ分析と前処理に強力な機能を提供します
- openpyxl:Excelファイルの作成、スタイリング、チャート作成に特化しています
実務ではこの3つを状況に応じて組み合わせて使用します。単純なデータ処理はpandasで、最終的なレポートはopenpyxlでスタイリングする方式が一般的です。
次回の第6編では、メールと通知の自動化を扱います。今回作成したレポートを自動でメールで送信したり、Slack/Telegramで通知を送る方法を学ぶ予定です。