# Pythonによる自動化 | LittleVoice-g-string

LittleVoice-g-string

苦しむために生きないであなた自身を愛してくれ

Pythonによる自動化


一、複数のcsvをExcelのSheetに保存する

1.目的

同じフォルダ内のcsvファイルに情報を追加し、合併する
フォルダごとの情報を一枚のExcel表にシートごとにまとめる

2.事前説明

①使ったモジュール

1
2
3
4
5
import os
import pandas as pd
import datetime
import py7zr
import openpyxl

②データ構造

1_毎日同じ構造のデータが生成すると想定する

1
2
3
4
5
6
7
8
9
10
20200701.7z
▲フォルダ1
■csvファイル1
■csvファイル2
▲フォルダ2
■csvファイル1
■csvファイル2
▲フォルダ3
■csvファイル1
■csvファイル2

2_元データの中身


20200701.csvlog_20200701.csv
開始時間 サイズ(Byte)
2020/7/1 0:02:00 0
2020/7/1 0:06:00 32567889
2020/7/1 0:08:00 17869027
2020/7/1 0:10:00 3170165999
開始 処理時間 件数
2020/7/1 0:02:00 0:00:00 0
2020/7/1 0:06:00 02:27.1 32
2020/7/1 0:08:00 02:04.0 0
2020/7/1 0:10:00 0:00:00 -20

3_希望結果

3.解凍

①圧縮されたデータを解凍する

1
2
3
4
zipfilename = './20200701.7z'
files = py7zr.SevenZipFile(zipfilename,'r')
files.extractall(path = r'') #ここ解凍後フォルダのpathを空白にすれば、余計なディレクトリを生成しなくても済む
files.close()

②解凍後フォルダ内のパスをリストに格納する

1
2
3
4
5
6
7
8
9
10
11
12
13
path = zipfilename[2:10]
def read_file(path):
mm = [m for m,k,w in os.walk(path)]
kk = [k for m,k,w in os.walk(path)]
ww = [w for m,k,w in os.walk(path)]
file_path_1 = []
file_path_2 = []
for i in range(1,len(mm)):
path_1 = mm[i] + '/' + ww[i][0]
path_2 = mm[i] + '/' + ww[i][1]
file_path_1.append(path_1)
file_path_2.append(path_2)
return file_path_1,file_path_2

4.csvの操作

①ファイルを読み込み、「件数」列は-20に等しい行を削除

1
2
3
4
5
6
def make_dataframe(file_path_1,file_path_2):
df_1 = [pd.read_csv(file_path_1[k],encoding='cp932') for k in range(len(file_path_1))]
df_2 = [pd.read_csv(file_path_2[k],encoding='cp932') for k in range(len(file_path_2))]
df_2 = [k[k['件数'] != -20].reset_index() for k in df_2]
df_2 = [k.loc[:,['開始','処理時間','件数']] for k in df_2]
return df_1,df_2

②読み込んだ2つのdataframeを合併する
③サイズ(Byte)列の値をサイズ(MB)に換算する
④文字列である「処理時間」列をdatetime.time型(hour,minute,second)のデータに変える
⑤second単位に統一し、sumを求めたら、datetime型に戻らせる
⑥諸々の集計結果を最後の一行に追加しておく

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def make_file(df_1,df_2):
df = pd.concat([df_2,df_1],axis=1)
df['サイズ(MB)'] = df['サイズ(Byte)']/(1024**2)

ii = [pd.to_datetime(i).time() for i in df_2['処理時間']]
time_to_second = []
for i in ii:
if i.microsecond: #i.microsecond:int型
s = '%06d' % i.microsecond #int型を文字列に変更する際、頭の0をなくさないようにする
if int(s[0]) >= 5:
seconds = i.hour*3600 + i.minute*60+ i.second +1
time_to_second.append(seconds)
else:
seconds = i.hour*3600 + i.minute*60+ i.second
time_to_second.append(seconds)
else:
seconds = i.hour*3600 + i.minute*60+ i.second
time_to_second.append(seconds)
m,s = divmod(sum(time_to_second),60)
h,m = divmod(m,60)
sum_time='{0}:{1:02d}:{2:02d}'.format(h,m,s)

sum_num = sum(df['件数'])
sum_size = sum(df['サイズ(Byte)'])
sum_size_MB = sum_size/(1024**2)

df.loc['sum'] = ['',sum_time,sum_num,sum_size/1024**3,sum_size,sum_size_MB]

return df

5.excelに書き込む

1
2
3
4
5
6
7
def combine_to_excel(list_file_1,list_file_2,list_file_3):
list_name = ['list_file_1','list_file_2','list_file_3']
writer = pd.ExcelWriter('20200701.xlsx')
for i in list_name:
eval(i).to_excel(excel_writer = writer,sheet_name = i,index=False)
writer.save()
writer.close()

6.残り課題

pyファイルに変えておきたい。

二、2つのフォルダに差になったファイルを新しいフォルダにコピーする

1.差になったファイルを探し出す

1
2
3
4
5
6
7
8
9
10
11
12
import os
from shutil import copy

ll=[l for l,m,n in os.walk('./')]
nn = [n for l,m,n in os.walk('./')]

#多めにあるフォルダを判定
len(nn[2]) > len(nn[3])
#差になったファイルを判定
file = [i for i in nn[2] if i not in nn[3]]
#差になったファイルのパスをつける
path=[ll[2]+'/'+i for i in file]

2.新しいフォルダにコピー

1
2
3
4
5
save_dir = './data_new'
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
for i in range(len(path)):
copy(path[i],save_dir)

三、フォルダ内のファイルを一括に読み込み、特定のファイルのみ結合する

1.ファイルを一括に読み込み

1
2
3
4
5
6
7
8
9
10
import pandas as pd
import os
import numpy as np

datas = []
for info in os.listdir("フォルダのパス"):
domain = os.path.abspath(r"フォルダのパス")
info = os.path.join(domain,info)
data = pd.read_csv(info,encoding ='cp932')
datas.append(data)

2.特定のファイルのみ結合

indexで指定する

1
2
3
datas_tmp = []
for i in range(len(datas)):
datas_tmp.append(datas[:i] + datas[i+1:])

四、フラグつけ

あるデータ群に対してランダムに10件HOTフラグに付け、残りは全部NOTフラグにつける
上記のdatas_tmpをもとに実行する

1
2
3
4
5
df=pd.concat(datas_tmp[2])
data2=np.random.randint(3,100,(100,3))
df_data2=pd.DataFrame(data2,columns=['挿入','公式','データ'])
df_data2
ddf=pd.concat([df.reset_index()[['挿入','公式','データ']],df_data2],axis=0)

ddfを今回フラグつけてあげたいデータとする

1
2
3
4
5
6
7
8
9
10
11
12
import random
num=92
list_not=[0]*num
num_hot=10
list_hot=[1]*num_hot

list_all=list_not+list_hot
random.shuffle(list_all)

ddf['HOT']=list_all
ddf.loc[ddf['HOT']==1,'NOT']=0
ddf.loc[ddf['HOT']==0,'NOT']=1

五、辞書によるリスト要素の重複削除

setを使用するには、hashableの対象ではないと効かないので、そのままsetを使用することで、エラーが出てしまいます。
  1. 回答①
    • dict内すべての要素を一度重複があるかどうかを判断してリストに格納する方法です。
1
2
3
4
5
6
7
8
9
result = []
def unduplicate(result,data):
if data not in result:
result = result+[data]
return result
for i in test:
result = unduplicate(result,i)
result
#[{'a': 1}, {'a': 3}, {'b': 2}]
  1. 回答②
    • 回答①と本質的に変わらないのですが、reduceを使うことで、やや効率よくなる方法です。
    • reduceの基本構文
      • reduce(function, iterable, initializer)
      • 第1引数には2つの引数を取るfunctionを指定する必要があります。(必須)
      • 第2引数にはiterableを指定する必要があります。(必須)
      • 第3引数にはオプションですが、指定された場合、iterableの先頭に置かれ、iterableが空の場合のデフォルト値になります。また第1引数にはfunctionの代わりにラムダ式を使用する事もできます。
1
2
3
4
5
6
7
from functools import reduce 
def delete_duplicate(data):
func = lambda x,y :x +[y] if y not in x else x
data = reduce(func,[[],] +data)
return data
delete_duplicate(test)
#[{'a': 1}, {'a': 3}, {'b': 2}]
  1. 回答③
    • dictを無理矢理に文字列に変更し、set関数を使って重複削除を行ったら、eval関数を使ってdictに戻させる方法です。
    • eval関数の基本構文
      • eval(“式”, globals=None, locals=None)
      • 第1引数の”式”には、ダブルクオーテーションで囲った式を代入します。
      • 第2引数と第3引数には、任意でそれぞれグローバル、ローカルの「名前空間」を指定しますが、これらはeval関数の基本的な使い方については理解するにはそれほど重要ではないので、詳しい説明はここでは省略します。
      • eval関数の返り値としては、第1引数で指定した式の値が返されます。
1
2
3
4
5
6
def delete_duplicate(data):
dict_ed = set([str(i) for i in data])
data = [eval(i) for i in dict_ed]
return data
delete_duplicate(test)
#[{'b': 2}, {'a': 3}, {'a': 1}]
  1. 回答④ ★★★☆☆
    • 要素であるdictをtuple形式に変更し、setを使って重複削除し、dict関数を使ってtuple形式をdict形式に戻させる方法です。
    • この方法は2重dictの要素が入ってあれば適用しないので、その場合回答③を使えればと思います。
1
2
3
data = [dict(t) for t in set([tuple(i.items()) for i in test])]
data
#[{'b': 2}, {'a': 1}, {'a': 3}]

Welcome to my other publishing channels