乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

plotly

今回は地震データの可視化についてです
最近地震が多いので、plotlyで可視化してみました。


解説動画はこちら








データの入手先


気象庁の地震データベース

コードを動かしてみたい方は
CSVがダウンロードできるようなので
手元にダウンロードしてみてください。



データの読み込み

こちらのコードはGoogle Colabで動くようになっています。
動かしたい場合は Colabの画面左メニューから
フォルダマークをクリックすると
ファイル置き場が見えるので、そこにCSVファイルを
ドラッグなどで配置します。

ファイルを読み込みするには下記のコードです。

# 必要なライブラリのインポート
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import numpy as np
import warnings
warnings.filterwarnings('ignore')

file_path = "/content/地震リスト.csv"
df = pd.read_csv(file_path)
df.head()

df変数にデータが読み込まれると思います。



データの整形


CSVそのままのデータでは
うまく可視化が行えないため
可視化用にデータを加工する必要があります。

下記のコードを実行すると
可視化用のカラムなどが追加されます。


# データの前処理
s_order = ['震度1', '震度2', '震度3', '震度4', '震度5弱', '震度5強', '震度6弱', '震度6強', '震度7']
target = "トカラ列島近海"

def preprocess_data(df):
    # 緯度・経度の変換(度分秒から度へ)
    def convert_coordinate(coord_str):
        # 例: "29°28.2′N" -> 29.47
        if '°' in coord_str and '′' in coord_str:
            parts = coord_str.replace('N', '').replace('E', '').replace('S', '').replace('W', '')
            degree_part = parts.split('°')[0]
            minute_part = parts.split('°')[1].replace('′', '')
            return float(degree_part) + float(minute_part) / 60
        return float(coord_str)
    
    df['緯度_数値'] = df['緯度'].apply(convert_coordinate)
    df['経度_数値'] = df['経度'].apply(convert_coordinate)
    
    # 深さの数値化
    df['深さ_数値'] = df['深さ'].str.replace(' km', '').astype(float)
    
    # 震度の数値化
    s_mapping = {
        '震度1': 1, '震度2': 2, '震度3': 3, '震度4': 4, '震度5弱': 5,
        '震度5強': 5.5, '震度6弱': 6, '震度6強': 6.5, '震度7': 7
    }
    df['最大震度_数値'] = df['最大震度'].map(s_mapping)
    df['最大震度'] = pd.Categorical(df['最大震度'], categories=s_order, ordered=True)
    return df

# データの前処理を実行
df = preprocess_data(df)
df = df[df["震央地名"]==target]
df.head()



時系列の可視化


最初は時系列で
どれだけ地震が発生しているのかを
見てみましょう

日別、震度別で時系列で地震の回数を
表示してみます。

# 時系列分析(日付別)
df['日付'] = pd.to_datetime(df['地震の発生日'])
daily_shindo_counts = df.groupby(['日付', '最大震度']).size().reset_index(name='地震回数')

# 震度の順序を維持
daily_shindo_counts['最大震度'] = pd.Categorical(
    daily_shindo_counts['最大震度'], 
    categories=s_order, 
    ordered=True
)

# 震度別横並び棒グラフ
fig_time = px.bar(
    daily_shindo_counts,
    x='日付',
    y='地震回数',
    color='最大震度',
    title='日付別地震発生回数(震度別)',
    labels={'地震回数': '地震発生回数', '日付': '発生日'},
    category_orders={'最大震度': s_order},
    barmode='group'  # 横並びに表示
)

# レイアウトの調整
fig_time.update_layout(
    xaxis_tickangle=-45,
    height=600,
    width=1000,
    legend=dict(
        orientation="v",
        yanchor="top",
        y=1,
        xanchor="left",
        x=1.01
    )
)

fig_time.show()
スクリーンショット 2025-07-05 17.27.01

データは直近の1週間分しか無いようです。
7月前後で急増しているのが分かります。



箱ひげ図の可視化


次は箱ひげ図で
「トカラ列島近海」の詳細を見てみましょう。


震度ごとにマグニチュードをまとめると
このような感じになります。

# 震央地名 最大震度別での箱ひげ図

fig_box = px.box(df, 
                 x='震央地名', 
                 y='M',
                 color='最大震度',
                 title='震央地名別 マグニチュード分布(最大震度別)',
                 labels={'M': 'マグニチュード', '震央地名': '震央地名'},
                 category_orders={'最大震度': s_order})

fig_box.update_layout(
    xaxis_tickangle=-45,
    height=600,
    width=1000
)
fig_box.show()
スクリーンショット 2025-07-05 17.29.52
同じ震度でも、マグニチュードにはバラツキがありますが
マグニチュードが上がるにつれ、震度も大きくなっています。



地図表示

今後は地図で震度を表示してみましょう
OpenStreetMapを使用して、緯度軽度を用いて
地図に震度を表示させてみます。

# マップ表示用のデータ準備
df_map = df.copy()
df_map['緯度_数値'] = pd.to_numeric(df_map['緯度_数値'], errors='coerce')
df_map['経度_数値'] = pd.to_numeric(df_map['経度_数値'], errors='coerce')
df_map['M'] = pd.to_numeric(df_map['M'], errors='coerce')
df_map = df_map.dropna(subset=['緯度_数値', '経度_数値', 'M'])
df_map['最大震度'] = pd.Categorical(df_map['最大震度'], categories=s_order, ordered=True)

# 緯度経度と最大震度・Mを用いたmap表示(OpenStreetMap使用)
fig_map2 = go.Figure()

# 震度別に色分けしてプロット
震度_colors = {
    '震度1': '#90EE90',  # 薄緑
    '震度2': '#FFD700',  # 金色
    '震度3': '#FFA500',  # オレンジ
    '震度4': '#FF6347',  # トマト色
    '震度5弱': '#FF4500',  # 赤オレンジ
    '震度5強': '#FF0000',  # 赤
    '震度6弱': '#8B0000',  # 濃い赤
    '震度6強': '#4B0082',  # インディゴ
    '震度7': '#000000'   # 黒
}

for 震度 in s_order:
    if 震度 in df_map['最大震度'].values:
        subset = df_map[df_map['最大震度'] == 震度]
        fig_map2.add_trace(go.Scattermapbox(
            lat=subset['緯度_数値'],
            lon=subset['経度_数値'],
            mode='markers',
            marker=dict(
                size=subset['最大震度_数値'] ** 2 ,  # サイズ調整
                color=震度_colors.get(震度, '#000000'),
                sizemode='diameter'
            ),
            text=subset['震央地名'],
            hovertemplate='%{text}
' + '緯度: %{lat:.2f}
' + '経度: %{lon:.2f}
' + 'マグニチュード: %{customdata[0]}
' + '最大震度: %{customdata[1]}
' + '', customdata=list(zip(subset['M'], subset['最大震度'])), name=震度 )) fig_map2.update_layout( title='地震発生位置(最大震度・マグニチュード)- OpenStreetMap', mapbox=dict( style="open-street-map", center=dict(lat=df_map['緯度_数値'].mean(), lon=df_map['経度_数値'].mean()), zoom=8 ), height=700, width=1000 ) fig_map2.show()
スクリーンショット 2025-07-05 17.34.15

今回の地震は、かなり局所的に起きていることが分かります。
特に震度5以上が発生した箇所がかなり近く
この近辺は注意が必要な地域に見えます。


散布図表示


最後に
マグニチュード、震度、深さを
散布図で見てみましょう

# 深さとマグニチュードの関係
fig_scatter = px.scatter(
    df,
    x='深さ_数値',
    y='M',
    color='最大震度',
    size='最大震度_数値',
    hover_name='震央地名',
    hover_data={
        '地震の発生日': True,
        '地震の発生時刻': True
    },
    title='深さとマグニチュードの関係(最大震度別)',
    labels={'深さ_数値': '深さ (km)', 'M': 'マグニチュード'},
    category_orders={'最大震度': s_order}
)

fig_scatter.show()
スクリーンショット 2025-07-05 17.36.27


どの深さでも、満遍なく起きているように見えますね
地震自体は、いつ何時、どこでも起きてしまいます。



まとめ

緯度経度が記載されているデータは
plotlyなどを用いれば地図表示が比較的
簡単に行うことができます。

可視化の表現の幅が広がるので
覚えておくと良いかもしれませんね


今日はここまでです
それでは




最近、やけに地震が多いので
気になって調べて
データを取って可視化してみました。


解説動画はこちら


 
今回のコードは
気象庁の地震データを取得して
そのデータを可視化するコードです。

先にデータが必要になるので
気になった人はコードを実行してみて下さい。

Jupyter Notebook や Google Colab で
実行できると思います。


地震データを取得する



気象庁から地震の観測データを取得して
データフレームにします。

やりたい人は、30分くらいかかるので
気をつけてやって下さい。

リンクを取得
import re
import time
import pandas as pd
import requests
from tqdm import tqdm
from bs4 import BeautifulSoup

domain = "https://www.data.jma.go.jp/"
index_url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/index.html"
res = requests.get(index_url)
soup = BeautifulSoup(res.content, "html.parser")
eq_link = [i.get("href") for i in soup.find_all("a") if len(i.get("href"))==13]
print(len(eq_link))

リンクからデータを取得する
# 地震データをテキストから取得
def data_pick(text):
    row_data = []
    for row in [i for i in text.split("\n") if len(i)>1][2:]:
        row = row.replace("° ","°")
        for i in range(7,1,-1):
            row = row.replace(" "*i, " ")
        row = row.replace(":"," ")
        tmp = row.split(" ")
        row_data.append(tmp[:-1])
    return row_data

all_data = []
for day in tqdm(eq_link):
    url = "https://www.data.jma.go.jp/svd/eqev/data/daily_map/" + day
    #print(url)
    res = requests.get(url)
    soup = BeautifulSoup(res.content, "html.parser")
    time.sleep(3.971)
    text_data = soup.pre.text
    all_data += data_pick(text_data)
    #break

print(len(all_data))


データを加工してデータフレームにする
columns=["年","月","日","時","分","秒","緯度","経度","深さ(km)","M","震央地名"]
df = pd.DataFrame(all_data,columns=columns)
df = df[df["M"] != "-"].reset_index(drop=True)
df = df.astype({"M": float , "深さ(km)":int, "年":int,"月":int,"日":int})
df["M2"] = df["M"]//1
df["年月日"] = df.apply(lambda x : "{0}{1:02}{2:02}".format(x["年"],x["月"],x["日"]),axis=1)

def lat_lon_10(x):
    tmp = x.split("°")
    degree = int(tmp[0])
    minute = int(tmp[1].split(".")[0])
    second = int(tmp[1].split(".")[1].split("'")[0])
    # 度、分、秒を10進法で表現
    decimal_degree = degree + minute/60 + second/3600
    return decimal_degree

df["緯度10"] = df["緯度"].map(lat_lon_10)
df["経度10"] = df["経度"].map(lat_lon_10)
df["size"] = df["M2"] ** 3

うまく実行できたら
df という変数にデータが格納されると思います。

エラーが出る人は
ライブラリが足りなかったり
通信がうまくいっていなかったり
色々な事象が起きると思いますが
全部は対応出来ませんので
がんばって解決して下さい。


データを見てみる


データを取得できたら
データを見てみましょう。

変数 df に対して
条件を指定すれば
絞り込みをしてみる事ができます。

マグニチュード別の地震回数
df[["M2","震央地名"]].groupby("M2").count().sort_index(ascending=False)
スクリーンショット 2024-03-02 16.29.41

日本地図にプロットする


日本地図にプロットするには
緯度と経度が必要です。

df 変数にはカラムとして
作成しているので
地図に表示する事ができます。

import plotly.express as px

fig = px.scatter_mapbox(
    data_frame=df[df["M2"]>=5],
    lat="緯度10",
    lon="経度10",
    hover_data=["年月日","深さ(km)"],
    color="M2",
    size="size",
    size_max=20,
    opacity=0.3,
    zoom=4,
    height=700,
    width=1500)

fig.update_layout(mapbox_style='open-street-map')
fig.update_layout(margin={"r": 0, "t": 0, "l": 0, "b": 0})
fig.show()
output_88_0




千葉県だけにすると
こんな感じです。

output_106_0


色々な条件で絞り込んで
地図に反映させる事ができます。

最近は地震が多いので
何かが起きている可能性がありますね。

警戒を強める意味でも
データを見る価値はあるかもしれません。

今回は地震のデータを取得して
それを可視化するコードについてでした。

それでは。


今回はビットコインの価格を
予測してみたいと思います。

解説動画はこちら



さてここからはGoogleColaboratoryを使って
コードを実行していきましょう。

データをダウンロードする

最初はビットコインの価格データの
ダウンロードからです。

ビットコインの価格データは
ここにあります。
ダウンロードはここ
csvファイル名は bitflyerJPY.csv.gz


colab上で実行する場合は
!wget --no-check-certificate https://api.bitcoincharts.com/v1/csv/bitflyerJPY.csv.gz
これでファイルがダウンロードされ
デフォルトのフォルダに置かれます。


データを見てみる

次はライブラリの読み込みです。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly
import plotly.graph_objects as go
%matplotlib inline
早速データを読み込んでみましょう。

データフレームの作成
df = pd.read_csv('bitflyerJPY.csv.gz')
df.columns = ['datetime', 'price', 'amount']
データ量が多いので時間が掛かります。

データ量を見てみると
df.shape
(63655790, 3)
非常に多いことがわかると思います。

データはこんな形になっています。
df.head()
datetimepriceamount
01499155298296016.00.600000
11499155299296016.00.018600
21499155302296550.00.050000
31499155310296558.00.050000
41499155319296539.00.229197


日付、価格、取引量
というデータの様ですが
日付がUnixTimeStampなので
分かる形になおします。


Unixtimestampを日付に直す
df['datetime'] = pd.to_datetime(df['datetime'],unit='s')
インデックスをdatatimeindexに直す
df = df.set_index('datetime')
df.tail()
priceamount
datetime
2021-03-20 01:10:136385001.00.06000
2021-03-20 01:10:176386760.00.00101
2021-03-20 01:10:176386760.00.00499
2021-03-20 01:10:176386101.00.00100
2021-03-20 01:10:206389000.00.00100

これで見える様になりましたね。
20210320のデータまでありました。
データは秒単位で入っている様ですね。


直近のデータを可視化してみましょう。
matplotlibで終わりの1万行分を
可視化してみます。


matplotlibで可視化
plt.figure(figsize=(10,6))
df['price'].tail(10000).plot()
plt.show()
download

数時間分のデータですが
かなりの値動きがあることが分かります。


plotlyを使って可視化する

ここからはplotlyを使って
もっと細かく見ていきましょう。

plotlyでローソク足を表示させてみましょう。
データとしては
初値、高値、安値、終値
この4つが必要です。

pandasで簡単に集計できます。

resampleで集計間隔を決め
ohlcで四本値を計算できます。


日別の4本値に集計する
data=df['price'].resample('1D').ohlc()
可視化
fig = go.Figure(
         data= [go.Candlestick(
                   x = data.index, 
                   open=data['open'],
                   high=data['high'],
                   low=data['low'],
                   close=data['close'])
         ]
)
fig.show()
スクリーンショット 2021-03-20 16.25.16
plotlyはグリグリ動かせるので
ズームしたりして色々遊べます。

これでみると2017年くらいからのデータで
2019年あたりに欠損があるようです。





予測モデルを作ってみる

ここからは予測モデルを作っていきましょう。
今回使用するのはFaceBookの
Prophetというライブラリで
時系列データの予測を行うことができます。


FacebookProphetライブラリの読み込み
from fbprophet import Prophet
import warnings
warnings.simplefilter('ignore')


学習用のデータを作成します。
学習には予測値(y)と日付(ds)という
2列分のデータが必要です。

ここでは終値をyに日付をdsに
置き換えて学習データを作成しています。


予測用の学習データの用意
model_df = data[['close']].tail(365)
model_df = model_df.reset_index()
model_df = model_df.rename(columns={'datetime':'ds','close':'y'})
model_df.head()
dsy
02020-03-21688958.0
12020-03-22647262.0
22020-03-23719267.0
32020-03-24752500.0
42020-03-25741720.0



これで準備できました。
学習は簡単に行えます。

予測モデルの学習
model = Prophet()
model.fit(model_df)

予測は何日先まで予測するかの
数値を入れてあげるだけです。

ここでは7日先までを予測します。

予測と可視化
future_df = model.make_future_dataframe(7)
forecast = model.predict(future_df)
model.plot(forecast)
downloadのコピー

はい
結果が出ました。

1年分のデータを用いての予測は
上昇傾向が続くという結果のようです。

予測結果の部分をみてみましょう。


予測結果
forecast_df = forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']]
forecast_df = forecast_df.set_index('ds')
forecast_df.tail()
yhatyhat_loweryhat_upper
ds
2021-03-236.375499e+066.065328e+066.667189e+06
2021-03-246.443882e+066.149507e+066.721009e+06
2021-03-256.480789e+066.192598e+066.773611e+06
2021-03-266.522106e+066.250351e+066.805741e+06
2021-03-276.572089e+066.278178e+066.857975e+06

ここを可視化してみます。

forecast_df.tail(10).plot()
plt.show()
download

yhatが予測値
upperとlowerが上限下限です。

だいたいこの間に収まるだろうという予測です。

1年という長い期間を用いた場合は
上昇傾向が続く形になりました。

もっと短い期間ではどうでしょうか?
次は1ヶ月でやってみましょう。

予測用のデータの日数を
変えるだけです。


過去30日から次の日の価格を予想する
# 過去30日の値から次の日を予想する
model_df = data[['close']].tail(30)
model_df = model_df.reset_index()
model_df = model_df.rename(columns={'datetime':'ds','close':'y'})
model = Prophet()
model.fit(model_df)
future_df = model.make_future_dataframe(1)
forecast = model.predict(future_df)
model.plot(forecast)
download-2

最初落ち込みが激しいですが
これも上昇傾向ですね。

7日だとどうでしょうか?
download-1
これもやや上昇傾向にあるように思えます。




まとめ

トレンドが続くものとした予測であれば
良さそうな感じです。

ただしこれは参考値にしかなりません。

トレンドの変化があるところでの
予測は難しいと思うので
短い期間で学習、予測という形で
価格を予想する際の参考に
していただければと思います。

今回はこれまでです
それでは。

米失業保険申請者数のデータがあったので
可視化してみました。


解説動画はこちら



データはここにあります。

データソース:2020年03/28までの統計データ

米国労働省:

https://www.dol.gov/ui/data.pdf

アメリカ合衆国 労働省雇用管理局

https://oui.doleta.gov/unemploy/wkclaims/report.asp

これをcsvファイルにしておきます。
列は「N.S.A(日付)」「S.F.(申請数)」の2列分を用意します。




pandas を使ってデータを読み込みします。
import pandas as pd

df = pd.read_table("米失業申請者数データ20200328.csv")
df['day'] = pd.to_datetime(df['N.S.A'], format='%m/%d/%Y')
「N.S.A」列が日付の部分の文字列「m/d/y」形式になっているので
日付のデータに直します。

次に可視化ツールのplotlyの方ですが
普通はインストールされていないと思うので
インストールしていない方はpip install plotly
などでインストールしてください。

以下のコードがこのデータを可視化するコードです。
import plotly
plotly.offline.init_notebook_mode(connected=False)
data = [
    plotly.graph_objs.Scatter(x=df["day"], y=df["S.F."], name="Claims")
]
layout = plotly.graph_objs.Layout(
    width=1000, height=600,
    title="title",
    xaxis={"title":"Years"},
    yaxis={"title":"Claims"},
)
fig = plotly.graph_objs.Figure(data=data, layout=layout)
plotly.offline.iplot(fig, show_link=False) 

日付を横軸(x) , 申請者数を縦軸(y)にあてて
線グラフを作ります。

可視化の結果は・・・
スクリーンショット 2020-04-05 15.56.29

右上にplotlyのツール類が表示され、ズームインアウトや
選択など色々選んで操作できます。

一般的なpandas、matplotlibの可視化では
動かすことができないので
可視化された図表を動かして見ることのできる
plotlyはかなりの優れものです。

データの方を見てみると
1982年とリーマンショックのあった2009年に
かなり多くの申請があったことが見えました。

しかし2020年3月になると
ドーンと跳ね上がって・・・

スクリーンショット 2020-04-05 16.01.20

爆増しているのが見えます。
レポートの数値から見ると
失業申請者数は600万人を超えているみたいですね。

リーマンの時でさえ100万人に満たなかった訳で
今回の影響がどれだけヤバイのかがすぐにわかります。

このデータは3/28のデータ
今日は4/5なのでそこから1週間経ってるいるため
さらに倍増している可能性もあります。

一説によると
アメリカの労働人口の2割ほどが影響を受けているそうで
もし、日本でも同様の事態となると
数十万人 - 100万人単位で
影響を受ける方が増えると予想されます。

特にレストランやバー、映画館、ホテル、スポーツジム
などは営業停止しており
特定の製造業も工場の稼働停止をしています。

リーマンの時は稼働停止までは至っていなかったと思うので
10年前の大不況に比べると
はるかに大きな不況が目の前に来ています。

前回の余波は、自分も食らったので2年くらい
まともな仕事が無かったです。

今回、それを超えるようなことに
ならないことを願うばかりです。

エンジニアの対策としては
現状新規案件が白紙、撤回されたり
内定も取り消されるケースが増えているようです。

フリーランスになったり、転職活動は
かなり厳しくなると予想されるので
今雇用があるなら、そこでじっと騒動が収まるまで
耐えるしかないかなと思います。

頑張って乗り切れば
明るい未来が待っていると思いたい。

それでは。



 

このページのトップヘ