質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

3回答

2730閲覧

exec()で、特定の変数に代入処理が正常動作しない

H.K2

総合スコア88

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2021/10/19 18:23

前提・実現したいこと

現在、組織内の様々な場所で、様式がバラバラなExcel方眼紙(俗にいうネ申Excel)
が運用されています。その状態で、データの分析などを行うような風土にするという
圧力流れになってきているため、下記図のように、様々な様式から
一括でデータを抽出して、前処理などを実施し、一つのdataframeに集約
(した後、分析、可視化)しようと考えています。
イメージ説明

発生している問題・エラーメッセージ

ただ、ネ申Excelですので、そのままセル位置を指定して、ハイそうですか、
とデータが抜けるものばかりではなく、
例えば、下図のように、データの前処理を行わないと必要なデータが取れない箇所があります。
(あくまで例として簡単なものを示しました)
イメージ説明

そのため、パラメータファイルを用意して、そこにあらかじめ用意した前処理関数を指定することで、
必要な箇所に前処理を通すようにしようとしましたが、その際に、前処理関数を実行するため、Exec()
を使用したのですが、思うような動作になりません。(具体的には、前処理部分を通っているのに、
execが実行されてないような動作になります。)
そのため、execでは変数代入がきちんと動かないのではないかと思い、下記のような簡単な処理を書いてみたのですが、
この処理では数値が代入されるため、原因がわからない状態になっています。

execの動作確認に使用した簡易コード a = 1 b = 2 c = None d = None func = "extract_year_from_yyyymmdd" exec(f"c, d = {func}('{a}', '{b}')")

該当のソースコード

Python3

1 2def make_row_list_from_param(in_book, p_df): 3 """ 全パラメータの行値作成処理 4 parameterからDFを生成するための1行のリスト(値)を作成。あとでファイル単位で全シート分データを結合するために、ラベル名のリストもreturnする。 5 6 Args: 7 in_book (pandas.Excelfile): 抽出元ブックのオブジェクト(pandas) 8 p_df (pandas.DataFrame): パラメータデータのテーブル 9 Returns: 10 return_val_list(list): 各シート単位のパラメータ(値)のリスト 11 return_label_list(list): 各シート単位のパラメータ(表示ラベル)のリスト 12 return_column_list(list): 各シート単位のパラメータ(列名(df用))のリスト 13 """ 14 src_val = None 15 src_label = None 16 src_column = None 17 return_val_list = [] 18 return_label_list = [] 19 return_column_list = [] 20 in_data = in_book.parse(p_df.iloc[0].in_sheet_name, header=None) # in_sheet_nameがそろっている(1種類しかない)前提で0番目のシート名でparse(headerなし) 21 # print(data_src.iloc[2,7]) # test用 22 for i, data in enumerate(p_df.itertuples()): 23 if not data.pre_process is np.nan: # 前処理の関数が入っている場合 24 print(f"前処理関数が指定されているので、前処理:{data.pre_process}を実施します。") 25 execute_str = f"src_val, src_label = {data.pre_process}('{in_data.iloc[data.in_st_row-1,data.in_st_col-1]}', '{data.disp_label}')" 26 exec(execute_str) 27 src_val = [in_data.iloc[data.in_st_row-1,data.in_st_col-1]] # とりあえず前処理関数のやつはexecが正常動作しない(値を入れない)ので、後回し 28 src_label = [data.disp_label] # 29 src_column = [data.db_column] 30 print(f"実行の式:{execute_str}, 実行後の値:{src_val}, 実行後のラベル:{src_label}です。") 31 pass 32 else: 33 src_val = [in_data.iloc[data.in_st_row-1,data.in_st_col-1]] 34 src_label = [data.disp_label] 35 src_column = [data.db_column] 36 print(f"シート名:{p_df.iloc[0].in_sheet_name}で{data.in_st_row}行,{data.in_st_col}列のデータは{src_label}:{src_val}") 37 return_val_list += src_val 38 return_label_list += src_label 39 return_column_list += src_column 40 # print(i, data.db_table, data.db_column) 41 print(return_val_list, return_label_list) 42 return return_val_list, return_label_list, return_column_list 43 44def extract_qc_data_from_excel(file_list, sheet_list, param_df): 45 """ 品質データ抽出(excelから) 46 パラメータファイルに従い、品質データをexcelファイル(xls*)から抽出する。 47 :param file_list: 48 :param sheet_list: 49 :param param_df: 50 :return: 51 """ 52 result_list = [] # 最終結果のデータ格納用のリスト 53 label_list = [] 54 column_list = [] 55 for i, p in enumerate(file_list): 56 input_book = pd.ExcelFile(p) 57 # print(f"file:{p}") 58 val_list = [] 59 label_list = [] 60 column_list = [] 61 val_l = None 62 lbl_l = None 63 col_l = None 64 for sh in sheet_list: 65 # print(sh) 66 part_p = param_df.query(f"in_sheet_name==@sh") # queryは=ではなく==なので注意。代入ではなく比較 67 try: 68 val_l, lbl_l, col_l = make_row_list_from_param(input_book, part_p) 69 except (IndexError, ValueError) as e: 70 print(f"エラー:{e}, 直前の正常処理データ:{lbl_l[-1]}") 71 print(f"ファイル名:{p}は入力データ異常の為スキップします。") 72 continue 73 val_list = val_list + val_l 74 label_list = label_list + lbl_l 75 column_list += col_l 76 # part_p.in_st_row 77 # print(f"val_list:{val_list}, label_list:{label_list}") 78 79 # 各ファイルごとにdfの行を生成 80 duplicates_list = [k for k, v in collections.Counter(column_list).items() if v > 1] 81 if len(duplicates_list) >= 1: # 重複した要素が1つ以上ある場合 82 print(f"重複要素として、{duplicates_list}が検出されたので、終了します。") 83 break 84 85 result_list.append(val_list) 86 df = pd.DataFrame(result_list, columns=column_list) 87 df_label = pd.DataFrame(columns=label_list) 88 89 return df, df_label 90 91# メイン処理 92print("パラメータの読み込みモードを設定ください") 93# select_param_mode = "XXX" 94select_param_mode = input() 95print(f"{select_param_mode}") 96 97# paramater読込み 98folder_path = Path(select_param_mode) 99param_df = pd.read_excel("input_param.xlsx", sheet_name=select_param_mode, skiprows=1) 100param_df.head() 101 102sheet_list = param_df.in_sheet_name.unique() 103 104# ファイルからinput_paramに従い、各種データ読み込み(df化) 105file_list = [p for p in folder_path.iterdir() if p.is_file() and re.search(".xls", str(p)) and str(p.stem)[:2] != '~$'] 106 107df, df_label = extract_qc_data_from_excel(file_list, sheet_list, param_df) 108

試したこと

execの動作について確認した
関連サイトをチェックした

補足情報(FW/ツールのバージョンなど)

Python 3.8.7
pandas 1.3.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2021/10/19 23:41

ちと意味不明ですが、そのexecではなにをしてるんでしょうか
H.K2

2021/10/20 03:36

パラメータファイルに記載している前処理関数名をもとに前処理を実行させようとしているつもりでした。
guest

回答3

0

ベストアンサー

関数名(文字列)をもとに関数を呼びたいのであれば、excecを使わずに以下のようなコードでも実現できます。
Calling a function of a module by using its name (a string)
How do I detect whether a Python variable is a function?

Python

1import types 2 3def get_func( func_name): 4 g = globals() 5 if func_name not in g: 6 return None 7 f = g[func_name] 8 if not isinstance(f, types.FunctionType): 9 return None 10 return f 11 12def f1(): 13 print('f1') 14 15f3 = 123 16 17for func_name in ['f1', 'f2', 'f3']: 18 f = get_func(func_name) 19 print(f) 20 if f: 21 f()

投稿2021/10/20 02:32

can110

総合スコア38233

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TakaiY

2021/10/20 02:53

関数名文字列から関数の取得という要求だったのは気づきませんでした。 globalsを使っていますが、localsを使ったほうがよかったりするかもしれません。
can110

2021/10/20 03:00

コメントありがとうございます。 回答で参考ページをあげておきながら、ちゃんと読んでおらずどちらでもいいかと思ったのですが localsを使ったほうが良い理由を教えていただけると幸いです。
H.K2

2021/10/20 03:38

ありがとうございます。まさにご回答のようなことをやりたかったです。 (表現がうまくできず申し訳ないです????) 上記処理で実行できそうな感じがしますのでやってみます。
TakaiY

2021/10/20 04:54

「localsを使ったほうが良い理由」 この場合localの方がいいということではなく、どのように対象の関数を用意するかによってlocalsの方がいい場合があるんじゃないかたということでした。 classで提供してgetattrで取りだすというのもありかもしれません。
can110

2021/10/20 05:11

了解しました。 使える関数だけ自前の辞書に登録して用意しておくという手もありますし これは実装によりますね。
H.K2

2021/10/20 23:33

ありがとうございます。上記で実装出来ました。ただ、Excelファイルから関数名取得した場合、 空欄の時も、「if f:」のブロックに入ってしまうようです。 この場合、get_func()側でif文を増やす必要がありますでしょうか。
can110

2021/10/21 01:03

「関数名(文字列)が空なら」という判定を最初に追加するとよいかと思います。
H.K2

2021/10/21 01:32

ありがとうございます!承知いたしました。
guest

0

pythonの関数は第一級オブジェクトなので、変数に入れたり、引数に渡したり、返したりできます。
ですので、質問のコードは、

python

1func = extract_year_from_yyyymmdd 2c, d = func(a, b)

このように実行することができます。

関数名に関数オブジェクトが入っている(束縛されている)ということです。

投稿2021/10/20 02:08

TakaiY

総合スコア12657

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

H.K2

2021/10/20 03:37

ご回答ありがとうございます。直で実行する場合は上記で行けそうですが、ファイルにある関数の文字列を読み込んできた場合、上記ではできなさそうです。
TakaiY

2021/10/20 04:55

はい。ですので、can110さんの回答の方法が正解と思います。
H.K2

2021/10/20 23:30

ありがとうございます!
guest

0

投稿2021/10/19 23:57

y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

H.K2

2021/10/20 03:37

ご提示ありがとうございます! 読んでみます。
y_waiwai

2021/10/20 03:45

そもそも、文字列をコマンドとして実行させる、ということ自体筋が悪いので、そこらへんの考え方を変えるほうがいいかと思います #python独自の機能なんで他に使いまわしが効かない、与える文字列をすり替えられたら何でも好きにできる、というセキュリティリスクなどなど
H.K2

2021/10/20 23:29

なるほど。ありがとうございます。勉強になりました。そのあたりを考えられるほど、実現の手段が思いつきませんでした…。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問