回答編集履歴

2

質問を受けて追記

2022/11/17 09:56

投稿

TakaiY
TakaiY

スコア12747

test CHANGED
@@ -105,5 +105,99 @@
105
105
  avaraged_data.to_csv(output_file_path, index=False, encoding='utf-8-sig')
106
106
  ```
107
107
 
108
-
109
-
108
+ ---
109
+ 質問を受けて追記
110
+
111
+
112
+ >・2つ目のコードについて、「# csvを読み込んで、平均を算出したdfを返す」の「df = pd.read_csv(」の後ろの「file」を「def make_avaraged_data(filename):」の「filename」に変えたら実行できたのですが、これは関数名の後ろの引数は関数の中の処理の変数と一致しなければならないからという認識で合っていますでしょうか?
113
+
114
+ バグですね。 すみません。
115
+
116
+ 「これは関数名の後ろの引数は関数の中の処理の変数と一致しなければならないから」というより、この関数は、受けとったcsv(のファイル名)をDataframeに読み込んで、平均化し、結果のDataframeを返すものですから、read_csvには仮引数を渡さなければならないのです。
117
+
118
+ >もともとのコードだとここでforループを使っていたので変数とオブジェクトで2個の文字列を作成しなければいけませんでしたが、forが無くなったので1個で良くなり、さらに言えば「filename」でも「file」でもどちらでも良い?
119
+
120
+ もともとのコードでは、引数で受け取るのはファイル名のリストですから、そのままread_csv()には渡さず、リストをループで回したときのループ変数(file)を渡していたのです。
121
+
122
+
123
+ >・returnを使う意味と使い方(後ろにdataが来るかaveraged_dataが来るか)について
124
+ >正直リターンを使う意味についてイマイチピンと来ていないのですが下記のサイトに書いてあるようことから
125
+
126
+ 「returnを使う意味」がわからないというのがわかりません。
127
+
128
+ サイト見てみました。 説明のしかたとしてはありかもしれません。
129
+
130
+ 僕なりの理解と説明をしてみます。
131
+
132
+ 関数というのは、本来**引数を与えて実行すると値を返してくるもの** です。 なので、**returnがあるのが普通**です。
133
+
134
+ 平方根を計算する関数は、math.sqrt()ですが、これは引数の値の平方根を返してくる関数だと思うのが普通です。
135
+ 「r2 = math.sqrt(2)」とすると、r2には2の平方根(1.4142...)が入ります。このとき、math.sqrt()関数が値を返すのに必要なのがreturnです。
136
+
137
+
138
+ > 使い方(後ろにdataが来るかaveraged_dataが来るか)については1つ目のコードがもともと
139
+ 「return averaged_data」だったところを「return data」と変更したのは、後々read_data関数を変数に代入するときに使われる値がリスト「averaged_data」ではなくリスト「data」つまりcsvファイルの数だけ追加してまとめた方だからでしょうか?
140
+
141
+ 説明したように「returnの使いかた」というのと、「後ろにdataが来るかaveraged_dataが来るか」は関係がありません。
142
+ 「後ろにdataが来るかaveraged_dataが来るか」はその関数の仕様として何を返すべきかの問題です。
143
+ この関数は、たぶん、「ファイルのリストを受け取って、それぞれのファイルをDataframeに読み込み、平均化して返す」のが仕様でしょう。 そして、複数のものを返すのであれば、リストで返すのが自然です。
144
+ そうすると、もとのコードには2つの問題があります。
145
+ 1つは、averaged_dataを返していることです。averaged_dataは結果のリストではありません。ループの中で1つのDataframeを平均化した結果を格納したものです。 これは、ループの度に書き換えられていて、ループ終了後には、最後の1回の結果しか入っていません。 なので、返すべきはリストである「data」です。
146
+ 2つめは、その返すべきリスト(data)に入っているのが、平均化前のDataframeであることです。なので、dataにappendするのは、dfではなく、avaraged_dataにしなければなりません。
147
+
148
+
149
+ >・「averaged_data」という変数が「# csvを読み込んで、平均を算出したdfを返す」と「# 全ての入力ファイルについて処理を実施」の両方で使われているのですが、これは同じ変数名で2回使っているだけでどちらかを違う変数名にしても問題ないでしょうか?それとも意図的に同じ変数名なのでしょうか?上手く言い表せないのですが、同じ変数名だと二重定義みたくなってしまう気がするのですが、順番に処理されるので最初に「averaged_data」を変数を使った後に、2回目に使う時には上書きされて1回目の処理とは無関係になるから大丈夫なのでしょうか?
150
+
151
+ 変数にはスコープがあります。スコープというのは有効範囲のことです。
152
+ 関数は1つのスコープになっていて、仮引数と内部の変数は外のスコープに影響しません。なので、二重定義になったりはしません。
153
+ 2つのavaraged_dataは別ものなので「2回目に使う時には上書きされて1回目の処理とは無関係になるから大丈夫」ということでもありません。
154
+
155
+
156
+ > ・1つ目のコードはエラーが出たので直したら、エラーなく実行されたのですが、出力先のフォルダに何も格納されなかったのですが理由分かりますでしょうか?
157
+ >直したのは「averaged_data」が「av a raged_data」になっている部分があったので全て「averaged_data」に修正しました
158
+
159
+ それもバグですね。すみません。
160
+
161
+ 理由は見ただけではわかりません。
162
+ 出力先のフォルダに何も格納されていないということは、最後の出力のループがまわっていないということだと思うので、その前の段階で入るべきデータが入っていないのだと思います。
163
+ 要所で変数の値をprintするなどして、どこでデータがおかしくなっているのか確認してみたいところです。
164
+
165
+ >・zip関数は調べたのですが「複数のリストを関数の引数にするためのもの」という認識でよろしいでしょうか?
166
+
167
+ zipは複数のリストから順に1つずつ取って新しいリストにして順に返す関数です。
168
+ 以下のコードを実行してみると動きがわかると思います。
169
+
170
+ list(zip([1, 2, 3], ['a', 'b', 'c']))
171
+
172
+
173
+ >「for input_file_path, averaged_data in zip(data_files, input_data_A):」の部分はオブジェクトの「data_files」と「input_data_A」は両方とも大元は「p_A.glob("*.csv")」で持ってきたフォルダ内のcsvファイル名、forループさせるには、オブジェクト = コード内には書かれていない物(ファイル名など)を変数で定義して、その変数を後の処理で使う形にしないといけない決まりになっている、今回は「input_file_path」という変数を後ろにstemを付けて出力ファイルの名前付けに利用したい、一方で平均化したリスト(csvファイルの中に欲しい値)は「read_data関数」を「input_data変数」に定義して、forループで回すためにさらにそれを「averaged_data変数に」定義し直しているということでしょうか?
174
+ >「出力ファイル名称付け」と「平均化したリストのCSV出力」をするには「2つの変数が必要」の理由がイマイチ分かっていませんが。
175
+
176
+ どうしてそのコードになったか説明するとわかるかもしれません。
177
+
178
+ まず、出力したいデータは「input_data_A」リストに入っていることはおわかりですよね。このリストには平均化したDataframeが入っています。であれば、
179
+ ```python
180
+ for avaraged_data in input_data_A:
181
+ avaraged_data.to_csv(<出力ファイルパス>)
182
+ ```
183
+ とすれば、結果が出力できます。
184
+ このとき、出力ファイルパスはどうすれば得られるでしょう(この質問の元ネタですね)。作るには元のcsvの名前が必要になということでしたよね。ところが、このfor分の中には、元のcsvの情報がありません。 平均化したデータには元のファイルの名前は付いていません。
185
+
186
+ どうすればいいかと考えたとき、元のcsvのリスト(data_files)を使えないかと思うわけです。そのリスト(data_files)と(input_data_A)の並びが同じです。
187
+ data_files = ['0000583A.CSV', '0000583B.CSV', ...]
188
+ input_data_A = [<0000583A.CSVを平均化したもの>, <0000583B.CSVを平均化したもの>, ...]
189
+ のような感じです。
190
+
191
+ 先程のループでは、input_data_Aで回していますので、対応するdata_filesにあるファイル名が取れればいいわけです。
192
+ 方法はいくつかありますが、今回は、先に出たzip関数を使いました。
193
+
194
+ zip(data_files, input_data_A) とすると、
195
+ [['0000583A.CSV', <0000583A.CSVを平均化したもの>], ['0000583B.CSV', <0000583B.CSVを平均化したもの>,]...]
196
+ というような、組み合わせのリストになって、
197
+
198
+ ```python
199
+ for input_file_path, avaraged_data in zip(data_files, input_data_A):
200
+ ```
201
+ とすると、 input_file_path と avaraged_data に順にファイル名と 平均化したデータが割り当てられてループするようになり、そのファイル名から<出力ファイルパス>を作ることができるようになるわけです。
202
+
203
+

1

追記

2022/11/17 01:56

投稿

TakaiY
TakaiY

スコア12747

test CHANGED
@@ -16,6 +16,94 @@
16
16
 
17
17
  という感じになります。
18
18
 
19
-
19
+ ---
20
+ 追記
21
+
22
+ だいぶ迷走してしまったので、こちらで直してみたコードです。
23
+ データが無いので動かしていませんから、バグはあると思います。
24
+ 動かないとか、読んでみてわからないなどあれば質問ください。
25
+
26
+ ■1つめは、 元の処理に近いもの
27
+ ・ read_data関数の内容は見てみたところ、作ったリスト(data)を返していないし、最後のavaraged_dataしか返しておらず、意図不明だったので、 avaraged_dataのリストを返すように直しました。
28
+ ・出力処理のところはほぼ書き直しています。read_dataがファイルのリストを受け取って結果しか返さないので、結果だけでは出力に必要なデータ(入力ファイル名)がないのでzipをつかって結合するなどしています。
29
+
30
+ ```python
31
+ from pathlib import Path
32
+ import pandas as pd
20
33
 
21
34
 
35
+ # 複数のcsvデータを読み込んで平均化する関数
36
+ #read_dataという名前の関数を作成する、filenameは引数であり関数の処理に渡される値
37
+ def read_data(filename):
38
+ # dataという名前のリストを作成する処理を行う
39
+ data = []
40
+ # fileが変数、filenameがオブジェクト、のループ処理を行う、次の読み込み処理でfileという変数にfilenameというオブジェクトの中の要素(結合したいcsvファイル名)を入れていく
41
+ for file in filename:
42
+ # 処理内容、今回はcsvファイルをpythonに読み込む、pd.read_csvでcsv形式のファイルをPandasのDataFrameへ読み取ってfile_dataという要素名に定義する、fileにはcsvファイル名が入る、headerは先頭行を3行目に指定、encodingは文字コード指定
43
+ df = pd.read_csv(file, encoding="cp932",skiprows=2,sep=',',index_col=0,parse_dates=True)
44
+ #averaged_dataという名前のリストを作るための関数を行う、
45
+ averaged_data = df.resample('3S').mean()
46
+ #定義された要素名file_dataをdataリストに追加
47
+ data.append(avaraged_data)
48
+ return data
49
+
50
+
51
+
52
+ #パスA: 入力ファイルの格納ディレクトリ
53
+ p_A = Path("Aフォルダ")
54
+
55
+ # glob関数を用いることでp_A = Pathのフォルダの中のcsv形式の
56
+ # ファイル名を取得してdata_filesという名前でリスト化する
57
+ data_files = p_A.glob("*.csv")
58
+
59
+ # 上記で作成したread_data関数にdata_filesを引数に指定して実行
60
+ # 実行結果を input_data_Aに格納
61
+ input_data_A = read_data(data_files)
62
+
63
+
64
+ # データの書き込み
65
+ # 出力ディレクトリ
66
+ p_B = Path("Bフォルダ")
67
+
68
+ # 計算結果のinput_data_Aにファイル名の情報が入っていないので、かなり苦しい
69
+ # 入力ファイルのリスト(data_file)の並びと、結果ファイルの並びが同じであることを前提に処理
70
+ for input_file_path, avaraged_data in zip(data_files, input_data_A):
71
+ output_file_path = p_B / (input_file_path.stem + "c.csv")
72
+ avaraged_data.to_csv(output_file_path, index=False, encoding='utf-8-sig')
73
+ ```
74
+
75
+ ■2つめは、この処理に合うように書き換えたもの
76
+ ファイルを読んで、平均を計算して、結果を出力するというループにしました。
77
+ また、変数名/関数名も適切と思うようなものに改めています。
78
+
79
+ ```python
80
+ from pathlib import Path
81
+ import pandas as pd
82
+
83
+ # csvを読み込んで、平均を算出したdfを返す
84
+ def make_avaraged_data(filename):
85
+ # csvデータを読み込む
86
+ df = pd.read_csv(file, encoding="cp932",skiprows=2,sep=',',index_col=0,parse_dates=True)
87
+ # 平均を算出
88
+ averaged_data = df.resample('3S').mean()
89
+ return avaraged_data
90
+
91
+ # 入力ディレクトリ
92
+ p_A = Path("Aフォルダ")
93
+ # 出力ディレクトリ
94
+ p_B = Path("Bフォルダ")
95
+
96
+ # 入力ファイルのリスト
97
+ input_files = p_A.glob("*.csv")
98
+
99
+ # 全ての入力ファイルについて処理を実施
100
+ for input_file_path in input_files
101
+ # データの平均を算出
102
+ avaraged_data = make_avaraged_data(input_file_path)
103
+ # データの書き込み
104
+ output_file_path = p_B / (input_file_path.stem + "c.csv")
105
+ avaraged_data.to_csv(output_file_path, index=False, encoding='utf-8-sig')
106
+ ```
107
+
108
+
109
+