#author("2025-03-07T18:19:12+01:00","","") #author("2025-03-07T18:33:22+01:00","","") [[技術資料]] *目次 [#m8988ea1] #CONTENTS *目的 [#q895f5ba] 産業連関表を活用した為替変動による波及効果の分析, 金融経済データとの相関分析, また3Dグラフによるサプライチェーン構造の可視化を行う. *使用するファイル [#pe64d1b6] 本研究で使用するファイルはすべてGoogleDriveにある. &br; user:iie.lab.tpu.2324@gmail.com &br; 「マイドライブ」→「学生」→「24_r2戸田」→「保存期間5年」→「3)卒論(プログラム)」の中にある。 &br; すべてのzipをダウンロードする.~ &color(red){中山実行プログラム}; #ref(中山実行プログラム.zip) ~ |フォルダ名|用途|ファイル名|ファイルの場所| |kawasehendou|為替変動の影響分析|''&color(red){7to12_Yfinance.ipynb};''|kawasehendou| |kabuka|株価データのデータ収集|''&color(red){7to12_JPX401.ipynb};''|kabuka| |sankakuka_2|産業連関表の三角化|''&color(red){sankakuka_2020.py};''|sankakuka_2| |soukan|相関ヒートマップの作成|''&color(red){soukan_7to12_map_yfinance.ipynb};''|soukan| |3Dgraph|jsonファイルの作成|''&color(red){3Dgraph_kihon.ipynb};''|3Dgraph| |3Dgraph|3Dグラフを可視化するための実行ファイル|app_0114.py|3Dgraph| |3Dgraph|javascriptで読み込む用のjsonファイル|output_toda_kihon_zenbu.json|3Dgraph/static/json| |3Dgraph|3Dグラフのデータの可視化|index_0114.html|3Dgraph/templates| |3Dgraph|3Dグラフを作成するときのjavascriptのファイル|main2_0114.js|3Dgraph/static| ~ ***プログラムを修正する [#zb7feed2] 上記で''&color(red){あかで書かれているプログラム};''は,~ csvファイルを書きこんだり,読みこんだりするときに,絶対パスを使ってあるが,~ そのままでは,「''C:/Users/ta_na/''/...」になっているはずである.~ その「''C:/Users/ta_na/''」のところを,''すべてのプログラムにおいて,すべての箇所で,''~ 上記でフォルダを置いた絶対パスに修正する必要がある.~ 「Visual Studio Code」では,特定の文字列をすべて別の文字列に~ 置換する機能があるので,それを利用しよう.~ ***動作環境 [#x9020b74] Pythonのバージョンは3.12.4を用いる |ライブラリ名|バージョン| |selenium|4.27.1| |panads|1.3.5| |numpy|1.26.4| |yfinance|0.2.52| |matplotlib|3.9.2| |webdriver-manager|4.0.2| |beautifulsoup4|4.12.3| |seaborn|0.13.2| |pulp|2.9.0| |networkx|3.4.2| |flask|3.0.3| ~ モジュールのインストールはコマンドプロンプトでpip install モジュール名 バージョンまで指定する場合はコマンドプロンプトでpip install モジュール名==指定するバージョン でインストールする ~ *** 各プログラムを実行する [#ce4e9821] 各プログラムを,以下のとおり,''&color(red){順番に};''実行する.~ ――――――――――――――~ ''1. 7to12_Yfinance.ipynb''~ ''2. sankakuka_2020.py''~ ''3. 3Dgraph_kihon.ipynb''~ ''4. app_0114.py''~ ''5. 7to12_JPX401.ipynb''~ ''6. soukan_7to12_map_yfinance.ipynb''~ ――――――――――――――~ *産業連関分析 [#ea813e90] **投入係数行列の作成 [#i8722536] -7to12_Yfinance.ipynb import numpy as np import pandas as pd #2020年国産表37部門 df_kokusan = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/kawasehendou/kawasehendou/csv/kokusan2020.csv", header=None) # 3行目以降(インデックス 2 以降)、3列目以降(インデックス 2 以降)を取得 subset_df_kokusan = df_kokusan.iloc[3:,2:] # データ型を確認し、必要ならfloatに変換 subset_df_kokusan = subset_df_kokusan.astype(float) # インデックスをリセットして1からの連番にする subset_df_kokusan.reset_index(drop=True, inplace=True) # 現在のインデックスをリセット subset_df_kokusan.index = subset_df_kokusan.index + 1 # インデックスを1から始める # 列名をリセットして1からの連番にする subset_df_kokusan.columns = range(1, len(subset_df_kokusan.columns) + 1) #内生部門だけ抽出 # インデックスと列の39以降を削除(1始まりの場合) subset_df_kokusan = subset_df_kokusan.loc[:37, :37] print(subset_df_kokusan) 取得した産業連関表から内生部門(37×37の行列)を抜き出す. ~ #国内生産額の抽出 # インデックス48番の行を取得 row_48 = df_kihon.loc[48] # NaN を含む行を削除 kokunaiseisan_row = row_48.dropna() # 最初の2行と最後の1行を削除 kokunaiseisan_row = kokunaiseisan_row.iloc[2:-1] # データ型を確認し、必要ならfloatに変換 kokunaiseisan_row = kokunaiseisan_row.astype(float) # インデックスをリセットして1からの連番にする kokunaiseisan_row.reset_index(drop=True, inplace=True) # 現在のインデックスをリセット kokunaiseisan_row.index = kokunaiseisan_row.index + 1 # インデックスを1から始める print(kokunaiseisan_row) 産業連関表から国内生産額を抽出する. #粗付加価値額の抽出 # インデックス47番の行を取得 row_47 = df_kihon.loc[47] # NaN を含む行を削除 arahukakathi_row = row_47.dropna() # 最初の2行と最後の1行を削除 arahukakathi_row = arahukakathi_row.iloc[2:-1] # データ型を確認し、必要ならfloatに変換 arahukakathi_row = arahukakathi_row.astype(float) # インデックスをリセットして1からの連番にする arahukakathi_row.reset_index(drop=True, inplace=True) # 現在のインデックスをリセット arahukakathi_row.index = arahukakathi_row.index + 1 # インデックスを1から始める print(arahukakathi_row) 産業連関表から粗付加価値額を抽出する. # 国産品投入係数行列を計算 coefficient_matrix_kokusan = subset_df_kokusan.div(kokunaiseisan_row, axis=1) coefficient_matrix_kokusan 国産品投入係数行列を作成 # 粗付加価値率(国内生産額÷粗付加価値額)を計算 coefficient_matrix_arahukakathi = arahukakathi_row / kokunaiseisan_row coefficient_matrix_arahukakathi #こっちのyfinanceのドル円レートを使う import yfinance as yf import pandas as pd # USD/JPYの為替データを取得 usd_jpy = yf.download("JPY=X", start="2024-07-01", end="2024-12-01", progress=False) # 終値を取得(Series にする)/ここから、変更が必要になる可能性あり usd_jpy = usd_jpy[['Close']].reset_index().set_index('Date').squeeze() # 結果を表示 print(type(usd_jpy)) # 結果を表示 print(usd_jpy) ※DataframeがSeriesに変換されない場合、各々で調べてコードを変える必要がある. Seriesに変換されていない場合、この先でエラーが発生する.~ ※Yahoo Finance(YF)のAPIやウェブサービスを使用してUSD/JPYのデータを取得しようとした際に、リクエスト数が多すぎるとエラーが発生する.~ →時間をおいて再度リクエストを試す ※''&color(red){yfinanceのバージョンを0.2.52};''に必ずする必要がある.~ # 一日の変動率を計算する(当日−前日)/前日 daily_change_rate = usd_jpy.pct_change() * 100 # 最初の1行を削除 daily_change_rate = daily_change_rate.iloc[1:] # 結果を表示 print(daily_change_rate) **輸入価格変動の影響 [#x8fea72e] # 米ドル建て輸入比率を辞書形式で定義 import_ratio_data = { "食料品・飼料": 56.2, "繊維品": 36.8, "金属・同製品": 79.6, "木材・同製品": 77.1, "石油・石炭・天然ガス": 97.3, "化学製品": 28.1, "はん用・生産用・業務用機器" : 39.3, "電気・電子機器" : 68.5, "輸送用機器" : 37.8, "その他産品・製品" : 62.6, "輸入計" : 64.4 } # データをリストに変換 import_ratio_list = [{"カテゴリ": category, "比率": ratio} for category, ratio in import_ratio_data.items()] # 米ドル建て契約比率 R_yunyu = pd.Series([56.2, 79.6, 56.2, 36.8, 77.1, 28.1, 97.3, 64.4, 62.6, 79.6, 79.6, 79.6, 39.3, 39.3, 39.3, 68.5, 68.5, 68.5, 37.8, 62.6, 64.4, 97.3, 64.4, 64.4, 64.4, 64.4, 64.4, 97.9, 64.4, 64.4, 64.4, 64.4, 64.4, 64.4, 64.4, 64.4, 64.4]) # 産業名リスト industry_names = [ "農林漁業", "鉱業", "飲食料品", "繊維製品", "パルプ・紙・木製品", "化学製品", "石油・石炭製品", "プラスチック・ゴム製品", "窯業・土石製品", "鉄鋼", "非鉄金属", "金属製品", "はん用機械", "生産用機械", "業務用機械", "電子部品", "電気機械", "情報通信機器", "輸送機械", "その他の製造工業製品", "建設", "電力・ガス・熱供給", "水道", "廃棄物処理", "商業", "金融・保険", "不動産", "運輸・郵便", "情報通信", "公務", "教育・研究", "医療・福祉", "他に分類されない会員制団体", "対事業所サービス", "対個人サービス", "事務用品", "分類不明" ] 輸入価格変動の影響を求める際に輸入品価格ベクトル Pim を,「為替の変動率×米ドル建て比率」として求めた. ~ 各産業の米ドル建て契約比率には日本銀行「輸出・輸入物価指数の契約通貨別構成比」の 2023 年 12 月の値を用いた https://www.boj.or.jp/statistics/outline/exp/pi/cgpi_2020/crcc2023.pdf ~ #輸入の影響 # 日付ごとの結果を格納する辞書 result_yunyu_dict = {} # 各日付ごとに計算を実施 for date, delta_E in daily_change_rate.items(): #print(delta_E) # Delta_P_im の計算 delta_P_im_yunyu = R_yunyu * (-delta_E) / 100 # 各日の為替変動率で計算 # 一次波及の計算 # delta_P_im に一致するインデックスを coefficient_matrix_yunyu から抽出 common_indices = coefficient_matrix_yunyu.index.intersection(delta_P_im_yunyu.index) sub_matrix = coefficient_matrix_yunyu.loc[common_indices] # 対応する行を抽出 delta_P_d_1 = sub_matrix.multiply(delta_P_im_yunyu[common_indices], axis=0) # 完全波及の計算 # 1. I - A_d^T を計算 I = np.eye(len(coefficient_matrix_kokusan)) # 単位行列 I_minus_Ad_T = I - coefficient_matrix_kokusan.T.values # 2. 逆行列を計算 I_minus_Ad_T_inv = np.linalg.inv(I_minus_Ad_T) # 3. A_mi^T を計算 A_mi_T = coefficient_matrix_yunyu.T.values # 4. ΔP_d を計算 delta_P_d_2 = I_minus_Ad_T_inv @ (A_mi_T @ delta_P_im_yunyu.values) # 結果を Pandas の Series として保存 delta_P_d_2 = pd.Series(delta_P_d_2, index=range(37)) # インデックスをリセットして1からの連番にする delta_P_d_2.reset_index(drop=True, inplace=True) # 現在のインデックスをリセット delta_P_d_2.index = delta_P_d_2.index + 1 # インデックスを1から始める # 付加価値を分母にしたものに変換 result_yunyu = delta_P_d_2 / coefficient_matrix_arahukakathi # インデックスを産業名に置き換える result_yunyu.index = industry_names # 結果を辞書に保存 result_yunyu_dict[date] = result_yunyu # 各日付の結果を表示(例) for date, result in result_yunyu_dict.items(): print(f"=== {date} ===") print(result) print("\n") #ref(nikkei2.png,,720x400) 以下のリンク先の資料(下の添付ファイル)の6,7ページ目の計算のプログラムコード https://www.jcer.or.jp/jcer_download_log.php?post_id=55288&file_post_id=55335~ #ref(産業連関表分析.pdf,,円安の産業連関表分析)~ # 産業・日付ごとのデータを DataFrame に変換 all_results_yunyu = [] for industry in industry_names: for date, result in result_yunyu_dict.items(): value = result[industry] all_results_yunyu.append({"日付": date, "産業": industry, "値": value}) result_df_yunyu = pd.DataFrame(all_results_yunyu) print(result_df_yunyu) # CSV 保存 #result_df_yunyu.to_csv("csv/out_data/industry_date_yunyu_2015_2020.csv", index=False, encoding="utf-8-sig") # 日付を統一形式に変換 result_df_yunyu['日付'] = pd.to_datetime(result_df_yunyu['日付'], format='%Y.%m.%d') # ピボットテーブルで変換 reshaped_df_yunyu = result_df_yunyu.pivot(index='日付', columns='産業', values='値') # 列名をリセットし、日付を列として戻す reshaped_df_yunyu.reset_index(inplace=True) reshaped_df_yunyu.rename(columns={'日付': 'Date'}, inplace=True) # 結果を表示 print(reshaped_df_yunyu) # CSV 保存 reshaped_df_yunyu.to_csv("csv/out_data/industry_date_yunyu_7to12.csv", index=False, encoding="utf-8-sig") 結果を見やすい形にしてcsvファイルに保存する. **輸出価格変動の影響 [#k13ded11] 輸出価格変動の影響を求める際に輸出価格の変化率を「為替の変動率×米ドル建て比率」として求めた. 輸入価格変動と同様に日本銀行「輸出・輸入物価指数の契約通貨別構成比」の 2023 年 12 月の値を用いる. # 日付を統一形式に変換 result_df_yusyutsu['日付'] = pd.to_datetime(result_df_yusyutsu['日付'], format='%Y.%m.%d') result_df_yunyu['日付'] = pd.to_datetime(result_df_yunyu['日付'], format='%Y.%m.%d') # 二つのデータフレームをマージ merged_df = pd.merge(result_df_yusyutsu, result_df_yunyu, on=['日付', '産業'], suffixes=('_輸出', '_輸入')) # 値を加算 merged_df['値_合計'] = merged_df['値_輸出'] + merged_df['値_輸入'] # 結果を表示 print(merged_df) # 元の産業順序を取得 original_order = merged_df['産業'].drop_duplicates().tolist() # ピボットテーブルで変換 reshaped_df_marge = merged_df.pivot(index='日付', columns='産業', values='値_合計') # 列の順序を元の順序に並び替え reshaped_df_marge = reshaped_df_marge[original_order] # 列名をリセットし、日付を列として戻す reshaped_df_marge.reset_index(inplace=True) reshaped_df_marge.rename(columns={'日付': 'Date'}, inplace=True) # 結果を表示 print(reshaped_df_marge) # CSV 保存 #reshaped_df_marge.to_csv("csv/industry_date_marge_2015_2020.csv", index=False, encoding="utf-8-sig") 輸出・輸入の価格変動の影響の差し引きを求める. # 指定した列を足し合わせて新しい列を作成 reshaped_df_marge['非鉄金属・金属製品'] = ( reshaped_df_marge['非鉄金属'] + reshaped_df_marge['金属製品'] ) reshaped_df_marge['はん用機械・生産用機械・業務用機械'] = ( reshaped_df_marge['はん用機械'] + reshaped_df_marge['生産用機械'] + reshaped_df_marge['業務用機械'] ) reshaped_df_marge['電気機械・電子部品・情報通信機器'] = ( reshaped_df_marge['電気機械'] + reshaped_df_marge['電子部品'] + reshaped_df_marge['情報通信機器'] ) reshaped_df_marge['対事業所サービス・対個人サービス'] = ( reshaped_df_marge['対事業所サービス'] + reshaped_df_marge['対個人サービス'] ) # 元の列を削除 reshaped_df_marge.drop( ['非鉄金属', '金属製品', 'はん用機械', '生産用機械', '業務用機械', '電気機械', '電子部品', '情報通信機器', '対事業所サービス', '対個人サービス', '水道', '廃棄物処理', '公務', '教育・研究', '他に分類されない会員制団体', '事務用品', '分類不明'], axis=1, inplace=True ) # 結果を表示 print(reshaped_df_marge) # CSV 保存 reshaped_df_marge.to_csv("csv/out_data/industry_date_marge_7to2.csv", index=False, encoding="utf-8-sig") 産業連関表における部門の分け方と業種別株価における業種の分け方とで違いがあるため, 産業連関データと業種別株価データを統合したデータセットの作成を行う. import matplotlib.pyplot as plt import numpy as np from matplotlib import rcParams # 日本語フォントを指定 rcParams['font.family'] = 'Meiryo' # Windowsの場合はMeiryoを推奨。Macではヒラギノを使用。 # 折れ線グラフのプロット plt.figure(figsize=(15, 6)) for column in reshaped_df_marge.columns: plt.plot(reshaped_df_marge.index, reshaped_df_marge[column], label=column) # グラフの設定 plt.title('為替変動による各産業への影響値の推移(2024年7月から12月)', fontsize=14) plt.xlabel('Date', fontsize=12) plt.ylabel('付加価値比', fontsize=12) # 凡例をグラフの外側に配置 plt.legend(title="産業", bbox_to_anchor=(1.05, 1), loc='upper left') plt.grid(True) # グラフを表示 plt.tight_layout() plt.show() #ref(output_1.png,,実行結果,,720x400) *3Dグラフの作成 [#eeca9a90] 産業連関表の三角化を行う. #ref(sankakuka2.png,,720x400) ~ -sankakuka_2020.py import numpy as np import pandas as pd from pulp import LpProblem, LpVariable, LpBinary, lpSum, LpMaximize, value import networkx as nx import matplotlib.pyplot as plt # 入力データの読み込み def read_input_data(file_path): # CSVファイルを読み込む df_yunyu = pd.read_csv(file_path) # セクター数を取得 #NSec = int(df.iloc[0, 1]) NSec = 37 # A行列を取得(2行目以降を数値行列として読み込む) #A = df.iloc[2:, 2:].replace({',': ''}, regex=True).values.astype(float) A = df_yunyu.replace({',': ''}, regex=True).values.astype(float) return NSec, A # 最適化問題の定義と解決 def solve_triangulation(NSec, A): problem = LpProblem("Triangulation", LpMaximize) # セクターの範囲 SSec = range(NSec) # 決定変数 X = [[LpVariable(f"X_{i}_{j}", cat=LpBinary) for j in SSec] for i in SSec] # 制約条件 for i in SSec: for j in SSec: if i < j: for k in SSec: if j < k: problem += X[i][j] + X[j][k] - X[i][k] >= 0, f"Transitivity1_{i}_{j}_{k}" problem += X[i][j] + X[j][k] - X[i][k] <= 1, f"Transitivity2_{i}_{j}_{k}" # 目的関数 LowSum = lpSum( (A[i, j] - A[j, i]) * X[i][j] + A[j, i] for i in SSec for j in SSec if i < j ) problem += LowSum # 問題を解く problem.solve() # 結果を取り出す Xo = np.zeros((NSec, NSec)) Rank = np.zeros(NSec) for i in SSec: for j in SSec: if i < j: Xo[i, j] = value(X[i][j]) elif i > j: Xo[i, j] = 1 - value(X[j][i]) Xo[i, i] = 1 Rank[i] = sum(Xo[i, :]) return Xo, Rank # 結果(rank)をファイルに出力 def write_output_data(file_path, Rank): df = pd.DataFrame({"Sector": np.arange(1, len(Rank) + 1), "Rank": Rank}) # インデックスをリセットして1からの連番にする df.reset_index(drop=True, inplace=True) # 現在のインデックスをリセット df.index = df.index + 1 # インデックスを1から始める # 産業名リスト industry_names = [ "農林漁業", "鉱業", "飲食料品", "繊維製品", "パルプ・紙・木製品", "化学製品", "石油・石炭製品", "プラスチック・ゴム製品", "窯業・土石製品", "鉄鋼", "非鉄金属", "金属製品", "はん用機械", "生産用機械", "業務用機械", "電子部品", "電気機械", "情報通信機器", "輸送機械", "その他の製造工業製品", "建設", "電力・ガス・熱供給", "水道", "廃棄物処理", "商業", "金融・保険", "不動産", "運輸・郵便", "情報通信", "公務", "教育・研究", "医療・福祉", "他に分類されない会員制団体", "対事業所サービス", "対個人サービス", "事務用品", "分類不明" ] # インデックスを産業名に置き換える df.index = industry_names df.to_csv(file_path, encoding="utf-8-sig") # Rank 列を昇順でソート df_rank_sorted = df.sort_values(by="Rank", ascending=True) df_rank_sorted.to_csv("C:/Users/ta_na/OneDrive/デスクトップ/s ankakuka_2/sankakuka_2/csv/out_data/Japan2020_37_rank_sort_kihon_tonyukeisu.csv", encoding="utf-8-sig") # index=False でインデックスを保存しない # 結果(行列X)をファイルに出力 def write_output_matrix(file_path, matrix): # 行列をデータフレームに変換して保存 df = pd.DataFrame(matrix) df.to_csv(file_path, index=False, header=False) # メイン処理 if __name__ == "__main__": # 入力ファイルと出力ファイル IN_FILE = "C:/Users/ta_na/OneDrive/デスクトップ/kawasehendou/kawasehendou/csv/coefficient_matrix_kihon.csv" OUT_FILE_RANK = "C:/Users/ta_na/OneDrive/デスクトップ/sankakuka_2/sankakuka_2/csv/out_data/Japan2020_37_rank_kihon_tonyukeisu.csv" OUT_FILE_X = "C:/Users/ta_na/OneDrive/デスクトップ/sankakuka_2/sankakuka_2/csv/out_data/Japan2020_37_X_kihon_tonyukeisu.csv" # データの読み込み NSec, A = read_input_data(IN_FILE) # 最適化の実行 Xo, Rank = solve_triangulation(NSec, A) # 結果を保存 write_output_data(OUT_FILE_RANK, Rank) write_output_matrix(OUT_FILE_X, Xo) print(f"Results saved to {OUT_FILE_RANK}") print(f"Results saved to {OUT_FILE_X}") 目的関数は, Aij と Aji の値を比べて, 産業部門 i と産業部門 j の上下関係を決め, 制約条件は, 産業部門を最適な順列に並び直す際に, 同じ順位の産業部門が存在しないようにするためのもの **jsonファイルの作成 [#c0352b1c] 3D Force-Directed Graphに産業部門間の関係の情報を送るためにjsonファイルを作成する.~ 産業部門間の関係が格納されたcsvファイルをjsonファイルに変換する -3Dgraph_kihon.ipynb import numpy as np import pandas as pd X = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/sankakuka_2/sankakuka_2/csv/out_data/Japan2020_37_X_kihon_tonyukeisu.csv", header=None) X import numpy as np import pandas as pd coefficient_matrix_kihon = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/kawasehendou/kawasehendou/csv/coefficient_matrix_kihon.csv") coefficient_matrix_kihon # X の 1 の部分に対応する coefficient_matrix_kihon の値を取り出し、 # X の 0 の部分は 0 のままにする coefficient_matrix_kihon_2 = X * coefficient_matrix_kihon # 出力結果 print(coefficient_matrix_kihon_2) # 新しいデータフレームを作成 rows = [] for i, row_label in enumerate(coefficient_matrix_kihon_2.index): for j, col_label in enumerate(coefficient_matrix_kihon_2.columns): if i != j and coefficient_matrix_kihon_2.iloc[i, j] != 0: # 対角要素をスキップ rows.append({'from': row_label, 'to': col_label, 'effect': coefficient_matrix_kihon_2.iloc[i, j]}) # 新しいデータフレームを作成 result_df = pd.DataFrame(rows) print(result_df) # CSVファイルに保存 result_df.to_csv('csv/out_data/from_to_kihon.csv', index=False, encoding='utf-8-sig') # 'effect'列の値を100倍 result_df['effect'] = result_df['effect'] * 100 #全部 import pandas as pd import csv import json # effectを削除 df_effect=result_df.drop(["effect"],axis=1) #fromとtoの中の要素を一つのリストにする count_id = df_effect.stack().drop_duplicates().tolist() # links の形式にデータを変換する links = [] for index, row in result_df.iterrows(): links.append({ "source": row['from'], "target": row['to'], "value": row['effect'] }) # データを指定された形式に変換 nodes = [{"id": item, "group": 1} for item in count_id] # JSON形式に変換 result = {"nodes": nodes, "links": links} # JSONファイルに保存 with open("static/json/output_toda_kihon_zenbu.json", "w") as f: json.dump(result, f, indent=2) 作成するjsonファイルの形式(output_toda_kihon_zenbu.json) { "nodes": [ { "id": "\u8fb2\u6797\u6f01\u696d", "group": 1 }, { "id": "\u98f2\u98df\u6599\u54c1", "group": 1 }, ~~~~~~~~~~省略~~~~~~~~~~ { "id": "\u60c5\u5831\u901a\u4fe1", "group": 1 }, { "id": "\u5bfe\u4e8b\u696d\u6240\u30b5\u30fc\u30d3\u30b9", "group": 1 } ], "links": [ { "source": "\u8fb2\u6797\u6f01\u696d", "target": "\u98f2\u98df\u6599\u54c1", "value": 19.05628066765477 }, { "source": "\u8fb2\u6797\u6f01\u696d", "target": "\u6559\u80b2\u30fb\u7814\u7a76", "value": 0.13824726106617 }, ~~~~~~~~~~省略~~~~~~~~~~ { "source": "\u5206\u985e\u4e0d\u660e", "target": "\u4ed6\u306b\u5206\u985e\u3055\u308c\u306a\u3044\u4f1a\u54e1\u5236\u56e3\u4f53", "value": 1.2702003066174 }, { "source": "\u5206\u985e\u4e0d\u660e", "target": "\u5bfe\u500b\u4eba\u30b5\u30fc\u30d3\u30b9", "value": 0.43188001994264 } ] } -"nodes"にはグラフに表示される単語の定義を行う.~ --"id"はノードの単語.~ --"group"は色分けなどをしたいときにノードのグループを指定する.~ -"links"には産業部門間の関係を記述する.~ --"source"は供給元の単語.~ --"target"は供給先の単語.~ --"value"は結ぶ線の大きさを変更するときなどに利用される.~ **3Dグラフ [#b7b070fc] 3Dグラフの描画にはThree.jsのモジュール”3D Force-Directed Graph”を使う.~ 参考にしたサイト👉https://vasturiano.github.io/3d-force-graph/~ javascriptの買い方はサイトを参考にすれば様々な変更が可能.~ ⚠モジュールのインポート方法はサイトのものでは行えなかったため独自で行った. -index_0114.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>3D Graph with Rankings</title> <style> body { margin: 0; padding: 0; display: flex; flex-direction: column; height: 100vh; } #container { display: flex; flex: 1; width: 100%; } #three { flex: 1; background-color: aliceblue; height: 100%; } #info { position: fixed; /* fixedに変更して、スクロールしても位置が固定されるようにする */ top: 10px; right: 10px; background-color: #fff; padding: 10px; border: 1px solid #ccc; border-radius: 5px; width: 300px; max-height: 900px; overflow-y: auto; z-index: 10; } #rankTable { position: fixed; /* 左上に固定表示 */ top: 10px; left: 10px; background-color: #fff; padding: 10px; border: 1px solid #ccc; border-radius: 5px; width: 300px; max-height: 900px; overflow-y: auto; z-index: 10; font-size: 12px; } /* レスポンシブ対応 */ @media screen and (max-width: 768px) { #container { flex-direction: column; } #rankTable { width: 100%; } #three { width: 100%; height: 400px; } } </style> </head> <body> <!-- 左上にランク表 --> <div id="rankTable"> <table> <thead> <tr> <th>産業部門名</th> <th>順列</th> </tr> </thead> <tbody> <tr><td>生産用機械</td><td>1</td></tr> <tr><td>医療・福祉</td><td>2</td></tr> <tr><td>教育・研究</td><td>3</td></tr> <tr><td>対個人サービス</td><td>4</td></tr> <tr><td>飲食料品</td><td>5</td></tr> <tr><td>農林漁業</td><td>6</td></tr> <tr><td>水道</td><td>7</td></tr> <tr><td>他に分類されない会員制団体</td><td>8</td></tr> <tr><td>分類不明</td><td>9</td></tr> <tr><td>公務</td><td>10</td></tr> <tr><td>輸送機械</td><td>11</td></tr> <tr><td>情報通信機器</td><td>12</td></tr> <tr><td>事務用品</td><td>13</td></tr> <tr><td>業務用機械</td><td>14</td></tr> <tr><td>建設</td><td>15</td></tr> <tr><td>はん用機械</td><td>16</td></tr> <tr><td>電気機械</td><td>17</td></tr> <tr><td>電子部品</td><td>18</td></tr> <tr><td>窯業・土石製品</td><td>19</td></tr> <tr><td>金属製品</td><td>20</td></tr> <tr><td>鉄鋼</td><td>21</td></tr> <tr><td>非鉄金属</td><td>22</td></tr> <tr><td>廃棄物処理</td><td>23</td></tr> <tr><td>繊維製品</td><td>24</td></tr> <tr><td>その他の製造工業製品</td><td>25</td></tr> <tr><td>パルプ・紙・木製品</td><td>26</td></tr> <tr><td>プラスチック・ゴム製品</td><td>27</td></tr> <tr><td>化学製品</td><td>28</td></tr> <tr><td>電力・ガス・熱供給</td><td>29</td></tr> <tr><td>石油・石炭製品</td><td>30</td></tr> <tr><td>鉱業</td><td>31</td></tr> <tr><td>商業</td><td>32</td></tr> <tr><td>運輸・郵便</td><td>33</td></tr> <tr><td>不動産</td><td>34</td></tr> <tr><td>金融・保険</td><td>35</td></tr> <tr><td>情報通信</td><td>36</td></tr> <tr><td>対事業所サービス</td><td>37</td></tr> </tbody> </table> </div> <!-- 真ん中に3Dグラフ --> <div id="three"></div> <!-- 右上にノード情報 --> <div id="info"> <!-- ノードの情報がここに表示されます --> </div> <!-- JavaScript ライブラリの読み込み --> <script type="module" src="https://unpkg.com/three@0.158.0/build/three.js" defer></script> <script type="module" src="https://unpkg.com/3d-force-graph@1.73.0/dist/3d-force-graph.min.js" defer></script> <script type="module" src="https://unpkg.com/three-spritetext@1.8.1/dist/three-spritetext.min.js" defer></script> <script src="./static/main2_0114.js" charset="utf-8" defer></script> </body> </html> --<script type="module" src="https://unpkg.com/three@0.158.0/build/three.js" defer></script>~ three.jsのインポート --<script type="module" src="https://unpkg.com/3d-force-graph@1.73.0/dist/3d-force-graph.min.js" defer></script>~ 3D Force-Directed Graphのモジュールのインポート --<script type="module" src="https://unpkg.com/three-spritetext@1.8.1/dist/three-spritetext.min.js" defer></script>~ テキストをノードにするときに必要なモジュールのインポート --<script src="./static/main2_0114.js" charset="utf-8" defer></script> プログラムに使うjavascriptのファイルの指定 ~ -main2_0114.js const highlightLinks = new Set(); const highlightNodes = new Set(); let hoverNode = null; const Graph = ForceGraph3D() (document.getElementById("three")) .jsonUrl('http://127.0.0.1:5000/static/json/output_toda_kihon_zenbu.json') .nodeColor('#ffffff') .nodeLabel('id') .nodeRelSize(20) .nodeThreeObject(node => { const sprite = new SpriteText(node.id); sprite.material.depthWrite = false; // make sprite background transparent sprite.color = node.color; // // 特定のノード名の色を変更 // if (node.id === '農林漁業') { // sprite.color = 'red'; // 特定ノードのテキスト色を赤に変更 // } else { // sprite.color = node.color; // 他のノードは元の色を使用 // } sprite.textHeight = 7.5; return sprite; }) .linkThreeObject(link => { // extend link with text sprite const sprite = new SpriteText(link.value.toFixed(2)); // 小数点1桁にフォーマット sprite.color = 'blue'; sprite.textHeight = 4.0; return sprite; }) .linkPositionUpdate((sprite, { start, end }) => { const middlePos = Object.assign(...['x', 'y', 'z'].map(c => ({ [c]: start[c] + (end[c] - start[c]) / 2 // calc middle point }))); // Position sprite Object.assign(sprite.position, middlePos); }) .onNodeClick(node => { // ノードクリック時に入出力リンクを表示 const outgoingLinks = Graph.graphData().links.filter(link => link.source.id === node.id); const incomingLinks = Graph.graphData().links.filter(link => link.target.id === node.id); // 左上に情報を表示 const displayArea = document.getElementById("info"); displayArea.innerHTML = `<h3>ノード: ${node.id}</h3>`; // 出ているリンク //displayArea.innerHTML += `<h4>Outgoing Links</h4>`; displayArea.innerHTML += `<h4>販売先</h4>`; outgoingLinks.forEach(link => { displayArea.innerHTML += `<p>${link.target.id} (Value: ${link.value.toFixed(2)})</p>`; }); // 向かってくるリンク //displayArea.innerHTML += `<h4>Incoming Links</h4>`; displayArea.innerHTML += `<h4>購入先</h4>`; incomingLinks.forEach(link => { displayArea.innerHTML += `<p>${link.source.id} (Value: ${link.value.toFixed(2)})</p>`; }); }) .onNodeHover(node => { // ノードにホバーした時の処理 hoverNode = node ? node.id : null; updateHoverText(); // テキストを更新する関数を呼び出す }) .linkOpacity(0.25) .linkDirectionalArrowLength(3) .linkDirectionalArrowRelPos(1) .linkCurvature(0.01) //.linkDirectionalParticleWidth(2) //.linkDirectionalParticles("value") //.linkDirectionalParticleSpeed(d => d.value * 0.01) .linkThreeObjectExtend(true) .linkColor(() => 'red') // リンクの色を黒に設定 .linkWidth(1) .backgroundColor("#f8f8f8"); // ノード間の力学的レイアウトを調整 Graph .d3Force("link").distance(300) // リンクの長さを100に設定(デフォルトは約30-50) .d3Force("charge").strength(300); // ノード間の反発力を調整(値を小さくすると密集する) function updateHoverText() { // ホバー時のノードのテキストサイズを変更 Graph.nodeThreeObject(node => { const sprite = new SpriteText(node.id); sprite.material.depthWrite = false; sprite.color = node.color; sprite.textHeight = hoverNode === node.id ? 15 : 7.5; // ホバー時にテキストサイズを変更 return sprite; }); } -ここで産業部門間の関係を記述したjsonファイルを指定している.~ .jsonUrl('http://127.0.0.1:5000/static/json/output_toda_kihon_zenbu.json') -実際に3Dグラフの描画の処理を行っている.~ ノードの色の変更の処理もここで行う.~ .nodeThreeObject -初期設定だとグラフの回転軸が中央で固定になってしまうため,今回はクリックしたノードを中心に回転できるようにしている. .onNodeClick -矢印の設定もろもろ .linkOpacity(0.25) .linkDirectionalArrowLength(3) .linkDirectionalArrowRelPos(1) .linkCurvature(0.01) .linkDirectionalParticleWidth(2) .linkDirectionalParticles("value") .linkDirectionalParticleSpeed(d => d.value * 0.01) .linkThreeObjectExtend(true) .linkColor(() => '#708090') .linkWidth(1) --linkDirectionalParticlesはノードの矢印にアニメーションを追加することで,矢印の向きが分かりやすいようにしている. -初期設定では背景の色が黒色だが見にくいため白色に変更している. .backgroundColor("#f8f8f8") ~ -app_0114.py ~ app_0114.pyを実行し、下の図のようなコマンドプロンプト内に表示されるURLをコピーし、ブラウザ上で検索する #ref(実行.PNG,,40%)~ 実際の出力結果 #ref(3Dgraph.png,,720x400)~ *データ収集 [#q47f7fcb] **産業連関表の収集 [#u7cb95fe] 産業連関表は以下の総務省のリンク先からダウンロードする ~ https://www.soumu.go.jp/toukei_toukatsu/data/io/2020/io20_00001.html #ref(総務省.png,,総務省 産業連関表,,720x400) ~ **スクレイピングによるデータ収集 [#ocdbc129] -7to12_JPX401.ipynb ~ 各企業のティッカーシンボル(株式会社で取引される企業を識別するためのコード)を取得するために、日本経済新聞のWebサイトからスクレイピングを行う. #ここからスタート #日本経済新聞のページからティッカーのみを取得する. from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager from bs4 import BeautifulSoup import time # Chromeドライバーのセットアップ service = Service("C:/Users/ta_na/OneDrive/デスクトップ/chromedriver-win64 (1)/chromedriver-win64/chromedriver.exe") driver = webdriver.Chrome(service=service) # URLを指定 url = "https://www.nikkei.com/markets/kabu/nidxprice/?StockIndex=N500" driver.get(url) # ページが完全に読み込まれるのを待つ time.sleep(3) # 繰り返し回数を指定 repeat_count = 10 # 例として3回繰り返す # ティッカー情報を格納するリスト tickers = [] # 繰り返し処理 for i in range(repeat_count): print(f"Page {i+1} のティッカーを取得中...") # BeautifulSoupを使ってページのHTMLを解析 soup = BeautifulSoup(driver.page_source, 'html.parser') # ティッカー情報を格納 for row in soup.find_all('tr'): # テーブル行を取得 columns = row.find_all('td') # 行内の列を取得 if len(columns) > 1: # 必要な列が存在する場合のみ処理 ticker = columns[0].text.strip() # ティッカー情報を取得 tickers.append(ticker) # 次のページに移動するためのクリックアクション try: # XPathを使って次ページボタンをクリック next_button = driver.find_element(By.XPATH, "//*[@id='CONTENTS_MARROW']/div[2]/div/div[2]/div/ul/li[7]/a") # ボタンのXPathを修正 next_button.click() # クリックアクション print(f"ページ {i+1} の次のページへ移動") except Exception as e: print(f"クリックアクションでエラーが発生しました: {e}") break # 次のページに移動できない場合、ループを終了 # 次のページの読み込みを待機 time.sleep(3) # 結果を出力 print("取得したティッカー一覧:") print(tickers) # 必要に応じてファイルに保存 with open("nikkei500_tickers_all.txt", "w") as file: for ticker in tickers: file.write(f"{ticker}\n") # ブラウザを閉じる driver.quit() ~ Pythonのライブラリであるyfinanceを用いてデータの収集を行う import yfinance as yf import pandas as pd import os output_dir = 'C:/Users/ta_na/OneDrive/デスクトップ/kabuka/kabuka/csv/out_data' # 業種ごとのデータを格納する辞書 industry_stock_data = {} industry_averages = {} # 1業種ずつ処理 for col, tickers in df_t1.items(): # df_tickers1 は辞書なので items() でキーと値を取得 tickers = [ticker for ticker in tickers if ticker is not None] # Noneを除いたティッカーのリスト ticker_close_prices = [] # その業種のティッカーに対応するClose価格を一時的に格納するリスト for ticker in tickers: if isinstance(ticker, str): # tickerが文字列である場合 ticker_symbol = ticker + ".T" # ティッカー番号に .T を追加して日本株式形式にする print(f"取得するティッカー: {ticker_symbol}") try: # yfinanceを使って指定期間の日足データを取得 data = {yf.download}(ticker_symbol, interval="1d", start="2024-07-01", end="2024-12-01", progress=False) ※''&color(red){yfinanceのバージョンを0.2.52};''に必ずする必要がある. # データが取得できた場合、Closeの値だけを格納 if not data.empty: ticker_close_prices.append(data['Close']) print(f"{ticker_symbol} の日足株価データのCloseのみ取得しました。") else: print(f"{ticker_symbol} のデータが見つかりませんでした。") except Exception as e: print(f"{ticker_symbol} の取得中にエラーが発生しました: {e}") else: print(f"無効なティッカー: {ticker}(スキップされました)") ※「データが見つかりませんでした」と出るが問題ない # データを統合して業種ごとのデータフレームを作成 if ticker_close_prices: industry_df = pd.concat(ticker_close_prices, axis=1) # 列数とティッカー数が一致しない場合、自動調整 if len(industry_df.columns) != len(tickers): print(f"警告: 列数の不一致。業種: {col} のティッカー数 {len(tickers)} と列数 {len(industry_df.columns)} が一致しません。") # 一致しない場合、列数に合わせてティッカーリストを調整 tickers = tickers[:len(industry_df.columns)] # 列名にティッカー番号を設定 industry_df.columns = tickers industry_df['Date'] = industry_df.index # 日付を追加 industry_df.set_index('Date', inplace=True) # 'Date' をインデックスとして設定 # NaNを削除(各ティッカーに対してNaNがある場合) industry_df = industry_df.dropna(axis=1, how='any') # NaNを含む列を削除 # 業種ごとの一日ごとの平均株価を計算 industry_df['Average'] = industry_df.mean(axis=1) # 業種ごとの平均株価データを industry_averages に格納 industry_averages[col] = industry_df['Average'] # 必要に応じてCSVファイルとして保存 file_path = os.path.join(output_dir, f"{col}_stock_data_with_average.csv") industry_df.to_csv(file_path) print(f"{col} のデータを '{file_path}' に保存しました。") else: print(f"{col} 業種のデータが存在しませんでした。") # 最終的な業種別平均株価データフレームを作成 df_heikin = pd.DataFrame(industry_averages) # 結果の確認 print("業種別平均株価データフレーム:") print(df_heikin) # 必要に応じてCSVとして保存 df_heikin.to_csv(os.path.join(output_dir, "industry_average_stock_prices.csv"), encoding="utf-8-sig") import yfinance as yf import pandas as pd 為替のデータを取得する # USD/JPYの為替データを取得 usd_jpy = yf.download("JPY=X", start="2024-07-01", end="2024-12-01", progress=False) # 終値を取得 usd_jpy_close = usd_jpy.iloc[:, 3] usd_jpy_close = usd_jpy.reset_index() ~ ※Yahoo Finance(YF)のAPIやウェブサービスを使用してUSD/JPYのデータを取得しようとした際に、リクエスト数が多すぎるとエラーが発生する.~ →時間をおいて再度リクエストを試す~ ※''&color(red){yfinanceのバージョンを0.2.52};''に必ずする必要がある. *相関 [#u016835a] -soukan_7to12_map_yfinance.ipynb import pandas as pd import numpy as np import matplotlib.pyplot as plt sangyou_df = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/kawasehendou/kawasehendou/csv/out_data/industry_date_marge_7to2.csv") daily_change_rate = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/kabuka/kabuka/csv/out_data/final2_7to12.csv") # Date列をdatetime型に変換 daily_change_rate['Date'] = pd.to_datetime(daily_change_rate['Date']) sangyou_df['Date'] = pd.to_datetime(sangyou_df['Date']) # 共通のDateを抽出 common_dates = set(daily_change_rate['Date']).intersection(set(sangyou_df['Date'])) # 共通のDateでフィルタリング filtered_daily_change_rate = daily_change_rate[daily_change_rate['Date'].isin(common_dates)] filtered_sangyou_df = sangyou_df[sangyou_df['Date'].isin(common_dates)] # 必要に応じてDateでソート filtered_kabuka_df = filtered_daily_change_rate.sort_values(by='Date') filtered_sangyou_df = filtered_sangyou_df.sort_values(by='Date') # 結果を表示 print(filtered_kabuka_df) print(filtered_sangyou_df) filtered_kabuka_df.to_csv("csv/out_data/final_kabuka_7to12.csv",index=False,encoding="utf-8-sig") filtered_sangyou_df.to_csv("csv/out_data/final_sangyou_7to12.csv",index=False,encoding="utf-8-sig") ドル円為替レートと日経 500 種平均株価それぞれの取引市場の営業日が異なっているな どの理由により, 取得できるデータの日付がドル円為替レートと業種別平均株価とで異なっているため, 両方とも取得できている日付のデータを抽出し. データフレームに格納する. ~ import pandas as pd import numpy as np import matplotlib.pyplot as plt sangyou = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/soukan/soukan/csv/out_data/final_sangyou_7to12.csv") #kabuka = pd.read_csv("C:/Users/pi/Desktop/soukan/csv/out_data/final_kabuka_7to12.csv") kawase = pd.read_csv("C:/Users/ta_na/OneDrive/デスクトップ/kabuka/kabuka/csv/out_data/final2_7to12.csv") # Date列をインデックスに設定 sangyou.set_index('Date', inplace=True) # すべての列名に (産業) を追加 sangyou.columns = [f"{col}(産業)" for col in sangyou.columns] sangyou # Date列をインデックスに設定 kawase.set_index('Date', inplace=True) kawase #Open, High, Low, Adj Close, Volume の列を削除 kawase = kawase.drop(columns=["('High', 'JPY=X')", "('Low', 'JPY=X')", "('Open', 'JPY=X')", "('Volume', 'JPY=X')"]) ※Pythonのバージョンによって、データのコラム名が異なるため、コラム名の部分を各々変える必要がある。("('High', 'JPY=X')"のような部分) # すべての列名に (株価) を追加 kawase.columns = [f"{col}(株価)" for col in kawase.columns] # Close を usdjpy に変更 kawase = kawase.rename(columns={"('Close', 'JPY=X')(株価)": "usdjpy"}) kawase.to_csv("csv/out_data/final_kabuka_usdjpy_7to12.csv",encoding="utf-8-sig") kawase # 横に結合(列方向) merge_df = pd.concat([sangyou, kawase], axis=1) merge_df データにコラム名を付ける. ~ import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt from matplotlib import rcParams # 日本語フォントを指定 rcParams['font.family'] = 'Meiryo' # Windowsの場合はMeiryoを推奨。Macではヒラギノを使用。 # 相関行列を計算(merge_dfは既に存在する前提) corr_matrix = merge_df.corr() # 相関行列をヒートマップとして保存 def save_corr_as_image_with_values(corr_matrix, file_name='correlation_matrix_2_2.png'): plt.figure(figsize=(12, 10)) # 図のサイズを大きく調整 # ヒートマップを作成 sns.heatmap(corr_matrix, cmap='Reds', annot=True, fmt=".2f", cbar=True, annot_kws={"size": 4}) # ラベルのフォーマット調整 plt.xticks(rotation=90, fontsize=8) # x軸ラベル plt.yticks(fontsize=8) # y軸ラベル # 余白調整と保存 plt.tight_layout() plt.savefig(file_name, dpi=300, bbox_inches='tight') # bbox_inchesでラベルの切れを防止 plt.close() # 関数を実行 save_corr_as_image_with_values(corr_matrix, 'figure/correlation_matrix_2_2.png') print("相関係数を表示した相関行列を画像として保存しました。") 相関を求めて、ヒートマップを作成. soukan/figureフォルダ内にcorrelation_matrix_2_2.pngが保存される 実行結果 #ref(correlation_matrix_2_2.png,,500x500)