Create polar color maps and line graphs from csv in python and automatically paste them into excel.
はじめに
こんにちは。
今回はいろいろなサイトを参考にしてエクセル作業を自動化するプログラムを作成しました。
作った経緯ですが円形のデータ整理を行っていると、表計算ソフトやらカラーマップ作成ソフトを使ってデータを可視化する場合もあります。それらをまとめとしてExcelに貼り付けたりすると、一つ一つ画像ファイルを挿入するのに時間がかかってしょうがない。。。
そこでcsvから極座標カラーマップと線グラフを作成してexcelに自動で貼り付けるプログラムをPythonで作ってみました。
サンプル
↓からサンプルがダウンロードできます。
https://github.com/MasayaUmezaki/polar_colormap_line
サンプルの中身はこんな感じになっています。datasにはサンプルデータが入っています。
↓動画でも紹介しています。
https://youtu.be/5cTBfeNySrs
動作環境はVScodeとAnaconda(Python3.9.7)で確認しています。
もし該当ソフトのインストールがお済みでない場合はリンク先からダウンロードできます。
VScode→https://code.visualstudio.com/Download
Anaconda3→https://www.anaconda.com/
プログラム内容
サンプルプログラムの内容を説明します。
まず作成される画像を置いておくルートディレクトリを作成します。
import os
#画像を置いておくルートディレクトリを作成
IMG_DIR = 'images'
COLORMAP_DIR = 'images/colormap/'
LINE_DIR = 'images/line/'
os.mkdir(IMG_DIR)
os.mkdir(COLORMAP_DIR)
os.mkdir(LINE_DIR)
実行するとプログラム動作時に画像の入るフォルダが作成されます。
次にカラーマップを作成します。コードは以下のようになっています。
"""
サンプルデータの取得→カラーマップ作成・保存
"""
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
# 定数設定
INPUT_SAMPLE_DATE_DIR = 'datas/sample' # データを置いておくルートディレクトリ
COLORMAP_DIR = 'images/colormap/' # カラーマップを保存するディレクトリ
def get_file_names(set_dir_name):
"""
ディレクトリ内のファイル名取得(ファイル名のみの一覧を取得)
"""
file_names = os.listdir(set_dir_name)
temp_full_file_names = [os.path.join(set_dir_name, file_name) for file_name in file_names if os.path.isfile(os.path.join(set_dir_name, file_name))] # ファイルかどうかを判定
return temp_full_file_names
def make_colormap_img(target_full_file_names):
"""
カラーマップの作成と保存
"""
target_full_file_names.sort() # ファイル名でソート
for target_file in target_full_file_names:
print( target_file + 'を作成')
basename = os.path.splitext(os.path.basename(target_file))[0] # ファイル名の部分だけを取得
df = pd.read_csv(target_file, names=("theta", "radii", "z_value"),header=0) # csvを読み込み
df = df.sort_values(by=["theta", "radii"]) # theta, radiiの順にソート
x = np.array(sorted(set(df.theta*np.pi/180))) # setで重複を除いたのち、thetaをソートする
y = np.array(sorted(set(df.radii))) # setで重複を除いたのち、radiiをソートする
z = np.array(df.z_value)
X, Y = np.meshgrid(x, y)
Z = z.reshape(len(x), -1).T # (x, y)の位置にデータを置くために、zをx軸の長さ×y軸の長さを持つ行列にする
plt.rcParams["figure.figsize"] = [3,4]
ax = plt.subplot(111, polar=True) # axesの設定
ax.set_rgrids(np.linspace(0, 150, 4), angle=20, fontname="Arial", fontsize=10) # グリッド作成
ax.set_title(basename, fontname="Arial", fontsize=14) # タイトル作成
ctf = ax.contourf(X, Y, Z, levels=np.linspace(100,250,31), vmin=100, vmax=250, cmap="jet") # コンター
colb = plt.colorbar(ctf, pad=0.12, orientation="horizontal", shrink=1.1) # カラーバー
colb.set_label("**", fontname="Arial", fontsize=12) # カラーバーのラベル
# 指定の場所に保存
plt.savefig(fname=COLORMAP_DIR+basename+".jpg", facecolor="white", bbox_inches='tight', pad_inches=0.05)
f_names = get_file_names(INPUT_SAMPLE_DATE_DIR) # ファイル名取得
make_colormap_img(f_names)
このコードは以下のサイトが大変参考になりました。
・matplotlibでcsvからpolar-colormap(極座標カラーマップ)を作成する - 湯船ったら湯船 (fc2.com)
http://0fulo.blog.fc2.com/blog-entry-27.html
実行するとカラーマップがimages/colormapに保存されます。
続けて線グラフを作成します。コードは以下のようになっています。
"""
サンプルデータの取得→線グラフ作成
"""
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
# 定数設定
INPUT_SAMPLE_DATE_DIR = 'datas/sample' # データを置いておくルートディレクトリ
LINE_DIR = 'images/line/'
def get_file_names(set_dir_name):
"""
ディレクトリ内のファイル名取得(ファイル名のみの一覧を取得)
"""
file_names = os.listdir(set_dir_name)
temp_full_file_names = [os.path.join(set_dir_name, file_name) for file_name in file_names if os.path.isfile(os.path.join(set_dir_name, file_name))] # ファイルかどうかを判定
return temp_full_file_names
def make_line_img(target_full_file_names):
"""
グラフの作成と保存
"""
target_full_file_names.sort() # ファイル名でソート
for target_file in target_full_file_names:
print( target_file + 'を作成')
basename = os.path.splitext(os.path.basename(target_file))[0] # ファイル名の部分だけを取得
df = pd.read_csv(target_file, names=("theta", "radii", "z_value"),header=0) # csvを読み込み
df = df.sort_values(by=["theta", "radii"]) # theta, radiiの順にソート
thetas = np.array(sorted(set(df.theta))) # setで重複を除いたのち、ソート
df_i = df.set_index(df.theta) # thetaでインデックス化
for a_theta in thetas:
d = dict()
d[a_theta] = df_i.loc[a_theta] # a_thetaをの要素を抽出
d[a_theta] = d[a_theta].sort_values(by=["radii"], ascending=False) # radii(半径)でソートして再代入
if a_theta < 180: # 180deg以下の時のみ処理
plus180 = a_theta + 180
d[plus180] = df_i.loc[plus180] # a_theta+180degの要素を抽出
d[plus180] = d[plus180].sort_values(by=["radii"]) # radii(半径)でソートして再代入
x0 = -d[a_theta].radii # 180deg以下のときradii(半径)にマイナスをかける
x180 = +d[plus180].radii
y0 = d[a_theta].z_value
y180 = d[plus180].z_value
x = np.hstack([x0,x180]) # 円の中心を通る一直線分のradiiを結合する
y = np.hstack([y0,y180]) # 円の中心を通る一直線分のz_valueを結合する
label = str(a_theta) + '-' + str(plus180)
plt.rcParams["figure.figsize"] = [3,3] # 画像サイズを調整
plt.xlabel("radii [**]", fontsize=10)
plt.ylabel("z_value [**]", fontsize=10)
plt.xlim(-160,160) # x軸範囲指定
plt.ylim(100,250) # y軸範囲指定
plt.plot(x,y,label=label)
plt.grid(c='gainsboro', zorder=9)
plt.legend(bbox_to_anchor=(1, 1.45), ncol=2)
# 指定の場所に保存
plt.savefig(fname=LINE_DIR+basename+"_line.jpg", facecolor="white", bbox_inches='tight', pad_inches=0.05)
plt.figure()
f_names = get_file_names(INPUT_SAMPLE_DATE_DIR) # ファイル名取得
make_line_img(f_names)
実行すると線グラフがimages/lineに保存されます。
最後に画像をエクセルにまとめます。※Anacondaの初期状態ではcv2のインポートが出来ないので、AnacondaPromptなどでopencvのインストールが必要です。
コードは以下のようになっています。
"""
result.xslxに画像の貼り付け
"""
import os
import glob
import imghdr
import openpyxl
import cv2
# 定数設定
IMG_DIR = 'images' # 貼り付ける画像を置いておくルートディレクトリ
SHEET_TITLE = 'result' # シート名の設定
RESULT_FILE_NAME = 'result.xlsx' # 結果を保存するファイル名
# 変数
max_width = [] # 各行の画像の高さの最大値を保持
def get_file_names(set_dir_name):
"""
ディレクトリ内のファイル名取得(ファイル名のみの一覧を取得)
"""
file_names = os.listdir(set_dir_name)
temp_full_file_names = [os.path.join(set_dir_name, file_name) for file_name in file_names if os.path.isfile(os.path.join(set_dir_name, file_name))] # ファイルかどうかを判定
return temp_full_file_names
def attach_img(target_full_file_names, set_row_idx, set_dir_name):
"""
画像を呼び出して、Excelに貼り付け
"""
set_column_idx = 1
ws.cell(row=set_row_idx, column=1).value = set_dir_name # 各列の1行目に、貼り付ける画像があるディレクトリ名を入力
max_height = 0 # 画像の高さの最大値を保持するための変数
target_full_file_names.sort() # ファイル名でソート
for target_file in target_full_file_names:
if imghdr.what(target_file) != None: # 画像ファイルかどうかの判定
img = openpyxl.drawing.image.Image(target_file)
column_letter = ws.cell(row=set_row_idx, column=set_column_idx+1).coordinate[:-1] # セルの行列番号から、そのセルの列番号の文字列を取得。最後の1文字は消す。
print('[' + str(column_letter) + '][' + str(set_row_idx) + ']' + target_file + 'を貼り付け')
# 画像のサイズを取得して、セルの大きさを合わせる(画像同士が重ならないようにするため)
size_img = cv2.imread(target_file)
height, width = size_img.shape[:2]
if max_height < height:
max_height = height
if not max_width[set_column_idx-1:set_column_idx]: # 配列「max_width」において、「set_column_idx」番目の要素が存在しなければ、挿入
max_width.insert(set_column_idx-1, width)
if max_width[set_column_idx-1] < width:
max_width[set_column_idx-1] = width
ws.row_dimensions[set_row_idx].height = max_height * 0.75
ws.column_dimensions[column_letter].width = max_width[set_column_idx-1] * 0.13
ws.column_dimensions['A'].width = 25
cell_address = ws.cell(row=set_row_idx, column=set_column_idx+1).coordinate # セルの行列番号から、そのセルの番地を取得
img.anchor = cell_address
ws.add_image(img) # シートに画像貼り付け
set_column_idx += 1
# ワークブック設定
wb = openpyxl.Workbook()
ws = wb.worksheets[0] # 1番目のシートを編集対象にする
ws.title = SHEET_TITLE # 1番目のシートに名前を設定
# 貼り付ける画像を置いておくルートディレクトリ内のディレクトリ名を再帰的に取得
dirs = glob.glob(os.path.join(IMG_DIR, '**' + os.sep), recursive=True)
row_idx = 1
# 各ディレクトリについて操作
for dir_name in dirs:
f_names = get_file_names(dir_name) # ファイル名取得
attach_img(f_names, row_idx, dir_name) # 画像貼り付け設定
row_idx += 1 # 次の列へ・・・
# ファイルへの書き込み
wb.save(RESULT_FILE_NAME)
このコードは以下のサイトが大変参考になりました。
・Pythonで大量の画像ファイルをExcelに貼り付け (cresco.co.jp)
https://www.cresco.co.jp/blog/entry/11916/
実行するとresult.xlsxが作成されます。
出力結果
極座標の半径、角度、Z値のデータから極座標カラーマップと線グラフを作成できました。
以上、プログラムのご紹介でした。ご一読ありがとうございました。