技術資料

目次 

目的 

産業連関表を活用した為替変動による波及効果の分析, 金融経済データとの相関分析, また3Dグラフによるサプライチェーン構造の可視化を行う.

使用するファイル 

本研究で使用するファイルはすべてGoogleDriveにある.
user:iie.lab.tpu.2324@gmail.com

「マイドライブ」→「学生」→「24_r2戸田」→「保存期間5年」→「3)卒論(プログラム)」の中にある。
すべてのzipをダウンロードする.

フォルダ名用途ファイル名ファイルの場所
kawasehendou為替変動の影響分析7to12_Yfinance.ipynbkawasehendou
kabuka株価データのデータ収集7to12_JPX401.ipynbkabuka
sankakuka_2産業連関表の三角化sankakuka_2020.pysankakuka_2
soukan相関ヒートマップの作成soukan_7to12_map_yfinance.ipynbsoukan
3Dgraphjsonファイルの作成3Dgraph_kihon.ipynb3Dgraph
3Dgraph3Dグラフを可視化するための実行ファイルapp_0114.py3Dgraph
3Dgraphjavascriptで読み込む用のjsonファイルoutput_toda_kihon_zenbu.json3Dgraph/static/json
3Dgraph3Dグラフのデータの可視化index_0114.html3Dgraph/templates
3Dgraph3Dグラフを作成するときのjavascriptのファイルmain2_0114.js3Dgraph/static


プログラムを修正する 

上記であかで書かれているプログラムは,
csvファイルを書きこんだり,読みこんだりするときに,絶対パスを使ってあるが,
そのままでは,「C:/Users/ta_na//...」になっているはずである.

その「C:/Users/ta_na/」のところを,すべてのプログラムにおいて,すべての箇所で,
上記でフォルダを置いた絶対パスに修正する必要がある.

「Visual Studio Code」では,特定の文字列をすべて別の文字列に
置換する機能があるので,それを利用しよう.

動作環境 

Pythonのバージョンは3.12.4を用いる

ライブラリ名バージョン
selenium4.27.1
panads1.3.5
numpy1.26.4
yfinance0.2.52
matplotlib3.9.2
webdriver-manager4.0.2
beautifulsoup44.12.3
seaborn0.13.2
pulp2.9.0
networkx3.4.2
flask3.0.3


モジュールのインストールはコマンドプロンプトでpip install モジュール名

バージョンまで指定する場合はコマンドプロンプトでpip install モジュール名==指定するバージョン でインストールする

各プログラムを実行する 

各プログラムを,以下のとおり,順番に実行する.

――――――――――――――

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
――――――――――――――

産業連関表の収集 

産業連関表は以下の総務省のリンク先からダウンロードする
https://www.soumu.go.jp/toukei_toukatsu/data/io/2020/io20_00001.html

総務省 産業連関表


スクレイピングによるデータ収集 

 #ここからスタート
 #日本経済新聞のページからティッカーのみを取得する.
 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)

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のデータを取得しようとした際に、リクエスト数が多すぎるとエラーが発生する.
→時間をおいて再度リクエストを試す

yfinanceのバージョンを0.2.52に必ずする必要がある.

産業連関分析 

投入係数行列の作成 

 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)
 # インデックスとカラムが 1~37 のデータを取得
 subset_df_kihon = subset_df_kihon.loc[1:37, 1:37]
 subset_df_kihon
 # 投入係数行列(全体)を計算
 coefficient_matrix_kihon = subset_df_kihon.div(kokunaiseisan_row, axis=1)
 coefficient_matrix_kihon.to_csv("csv/coefficient_matrix_kihon.csv", index=False, encoding="utf-8-sig")
 coefficient_matrix_kihon

産業連関表から粗付加価値額を抽出する.

 # 国産品投入係数行列を計算
 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のデータを取得しようとした際に、リクエスト数が多すぎるとエラーが発生する.
→時間をおいて再度リクエストを試す

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)

輸入価格変動の影響 

 # 米ドル建て輸入比率を辞書形式で定義
 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")
nikkei2.png

以下のリンク先の資料の6,7ページ目の計算のプログラムコード https://www.jcer.or.jp/jcer_download_log.php?post_id=55288&file_post_id=55335

 # 産業・日付ごとのデータを 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ファイルに保存する.

輸出価格変動の影響 

輸出価格変動の影響を求める際に輸出価格の変化率を「為替の変動率×米ドル建て比率」として求めた.

輸入価格変動と同様に日本銀行「輸出・輸入物価指数の契約通貨別構成比」の 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()
実行結果

相関 

 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が保存される

実行結果

correlation_matrix_2_2.png

3Dグラフの作成 

産業連関表の三角化を行う.

sankakuka2.png


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ファイルの作成 

3D Force-Directed Graphに産業部門間の関係の情報を送るためにjsonファイルを作成する.
産業部門間の関係が格納されたcsvファイルをjsonファイルに変換する

 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
       }
   ]
}

3Dグラフ 

3Dグラフの描画にはThree.jsのモジュール”3D Force-Directed Graph”を使う.
参考にしたサイト👉https://vasturiano.github.io/3d-force-graph/
javascriptの買い方はサイトを参考にすれば様々な変更が可能.
⚠モジュールのインポート方法はサイトのものでは行えなかったため独自で行った.

<!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>


 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;
     });
 }

    


app_0114.pyを実行し、下の図のようなコマンドプロンプト内に表示されるURLをコピーし、ブラウザ上で検索する

実行.PNG

実際の出力結果

3Dgraph.png

トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS