Closed victor-von-pooh closed 5 months ago
ありがとうございます.
Moji_analysis.ipynb
を更新しましたので,ご確認の程よろしくお願いいたします.
@tottocoslowlifer 確認しました. 非常に良くできていると思いました. 3点だけコメントさせて頂きます.
2つ目については関数を作成するのが良いかなと思います.
3つ目は少し問題があります. 理由としては, 潮位のデータの1日が0〜23時なのに対し, 下関の気象データは1〜24時になっているからです.
以上のご対応をよろしくお願い致します.
対応が完了いたしました. ご確認の程よろしくお願いいたします.
お疲れ様です. ご対応いただきありがとうございます. 大変申し訳ございませんが, 色々と考えた方がいいことがあるみたいなので, このセクションを最初からやり直しましょう.
まず, 前提として関数はセクション冒頭にまとめましょう. この際, 関数間は2行空けます. また, 返り値は基本的には用意しましょう.
順を追ってやってみましょう.
DataFrame
を作成する関数を作成するDataFrame
と月齢の DataFrame
を統合するDataFrame
と合わせるそれぞれでの注意事項を説明します.
DataFrame
を作成する関数を作成するDataFrame
DataFrame
DataFrame
これらを呼び出す関数を作成します.
DataFrame
6個目のセルの出力と同じものが出てくるような関数を用意しましょう. 基本的に6個目のセルまでにやったことをまとめれば良いので,
def call_moji_tide() -> pd.DataFrame:
df = pd.read_csv("../data/Moji_Tide_2011-2021/csv/9010_2011-2021.csv").drop("9010(門司の観測値コード)", axis=1)
date_cols = ["20xx年", "xx月", "xx日"]
df["年月日"] = [
datetime.date(
2000 + int(df["20xx年"][i]),
int(df["xx月"][i]),
int(df["xx日"][i])
)for i in range(len(df))
]
df = df.drop(date_cols, axis=1)
date_keys = []
for i in range(len(df)):
if df["年月日"][i] not in date_keys:
date_keys.append(df["年月日"][i])
am_cols = [f"{i}時の潮高値(cm)" for i in range(12)]
pm_cols = [f"{i}時の潮高値(cm)" for i in range(12,24)]
new_df_list = []
for i in range(len(date_keys)):
tmp = df[df["年月日"] == date_keys[i]].reset_index().drop("index", axis=1)
am_data = [item for item in tmp[tmp["1(午前)/2(午後)"] == 1][am_cols].values[0]]
pm_data = [item for item in tmp[tmp["1(午前)/2(午後)"] == 2][am_cols].values[0]]
new_df_list.append(am_data + pm_data)
time_cols = am_cols + pm_cols
df = pd.concat([pd.DataFrame(date_keys, columns=["年月日"]), pd.DataFrame(new_df_list, columns=time_cols)], axis=1)
date_dropped_df = df.drop("年月日", axis=1)
index = list(date_dropped_df.dropna(how="all").index)
return df.iloc[index].reset_index().drop("index", axis=1)
こんな感じで良いと思います.
DataFrame
こちらは,
df = pd.read_csv("../data/Moon_2011-2021/Mooncal_2011-2021.csv")
で生の DataFrame
を呼び出す["こよみ", "気象庁", "MIRC"]
の列について, それぞれのデータを {"小潮": 1, "中潮": 2, "大潮": 3, "若潮": 4, "長潮": 5}
のようにラベリングし, 整数値を格納する
ラベリング用の関数を作ると楽かも
def get_label(df: pd.DataFrame, col: str) -> pd.DataFrame:
labels_dict = {"小潮": 1, "中潮": 2, "大潮": 3, "若潮": 4, "長潮": 5}
col_list = df[col].to_list()
labels = [labels_dict[item] for item in col_list]
df = df.drop(col, axis=1)
df[col] = labels
return df
["年月日"]
を datetime.date
の型に直すという作業をしましょう.
こんな感じの DataFrame
ができると良いかと思います.
DataFrame
こちらは,
df = pd.read_csv("../data/Shimonoseki_2011-2021/Shimonoseki.csv").rename(columns={"Unnamed: 0": "年月日時"})
で生の DataFrame
を呼び出す["年月日時"]
を datetime.datetime
の型に直す
datetime.date
と datetime.time
を用意し, datetime.datetime.combine()
メソッドを使うと簡単datetime.timedelta
を使うと楽["降水量(mm)", "気温(℃)"]
について, float 型で扱えるようにデータそのものを書き換える
--
は0, ///
は欠損値, その他はその値(float 型)float("nan")
とする17.8)
などのエラーが含まれるのでこれをちゃんと通るように処理するのがポイントという作業をしましょう. こちらも
こんな感じの DataFrame
ができると良いかと思います.
一つのセルで,
df = call_moji_tide()
moon_df = call_mooncal()
shimonoseki_df = call_shimonoseki()
のように呼び出せると良いと思います.
DataFrame
と月齢の DataFrame
を統合するmerged_df = pd.merge(df, moon_df, on="年月日", how="inner")
おそらくこの1行で出来ます.
DataFrame
と合わせるここで, データを一元化する関数を作成しましょう.
引数に how
などを入れて, 同じ関数内に if 文の分岐を入れ,
これらを選択できるようにしましょう.
こんな感じの DataFrame
ができると良いかと思います.
すみません, 補足です.
次の日の値と差を取り, 24分割する
こちらについて, 最終日(2021-12-31)は欠損値ではなく前日と同じ差分を足して埋めてみましょう.
お疲れ様です.先ほど,notebookをコミットいたしました.
最後の
2で作ったデータをフラットにし, 気象の DataFrame と合わせる
に取り組んでいるところなのですが, 30個目のセルのように,meta_df
の年月日時
の型がTimestamp型?になってしまい,行き詰っているところです.
よろしくお願いいたします.
@tottocoslowlifer ありがとうございます. 迅速な対応で助かります.
全体的によく書けています. 関数の中身も的確です◎
以下コメントになります.
まずはコードを確認してのコメントになります.
import datetime as dt
とエイリアスをつけることがあるのは知らなかったです.
冗長な書き方が防げて良いですね◎
現在, import numpy as np
がどこにも使われていないようなので, この1文はコメントアウトしてしまいましょうか(一応後で出てくるかもしれないため).
from datetime import timedelta
は無くても良いかなと思います.
timedelta
を使うのが call_shimonoseki()
関数の上から9行目の1箇所だけなので, ここを
dt_ymd = dt.date(dt_ymd.year, dt_ymd.month, dt_ymd.day) + dt.timedelta(days=1)
とするのが良いと思います. どうしても必要であれば, 1個目のセルに入れましょう.
call_mooncal()
関数及び call_shimonoseki()
関数は非常に良く書けていて素晴らしいです◎
一方で, full_merge()
関数ですが,
call_shimonoseki()
関数の最後からもう1行空けるmoon_df
がどこにも出ていないdf
も入っているけれど, 上から5行目の1箇所のみしか出てきておらず, ここも merged_df["年月日"]
で代用可(なので要らない)という感じだと思います.
ただし, ここで使われた処理のアイデアは良く, これを応用して merged_df
をフラット(1時間毎の DataFrame
)にしましょう.
ここは確認として moon_df
を呼び出したのかと思われますが, アップロードのときは無くても大丈夫です.
もしくは, 3個セルを使って df
と shimonoseki_df
も表示させると良いかなと思います(moon_df
のみあるのが不自然という話でした).
これらは今調整中と見ています. 出来次第関数は24個目のセルの中に入れていきましょう.
30個目のセルのように,
meta_df
の年月日時
の型がTimestamp型?になってしまい,行き詰っているところです.
こちらに対する回答ですが, Timestamp 型になることが理想でした(なので間違ってはいないです).
ではどのようにすれば良いかということですが, 27個目のセル同様, pd.merge(df_1, df_2, on="年月日時", how="inner")
を使ってみましょう.
この df_1
には merged_df
をフラットにした DataFrame
を, df_2
には shimonoseki_df
を入れるという感じで繋げます.
このとき, pd.merge()
の2つの引数 on
と how
はそれぞれ,
on
: df_1
, df_2
に共通するカラムを基準に同じ要素に基づいてマージする, カラム名を指定how
: どのような入れ方をするか, "inner"
だと共通の要素のみに集約する(積集合), "outer"
だと少なくとも片方に入っている要素を全て入れる(和集合)という役割を担っています.
現在, merged_df["年月日"]
にも shimonoseki_df["年月日時"]
にも, Timestamp 型で要素が含まれているのでこのカラムを基準にマージできることを目指しましょう.
お疲れ様です.
月齢データと門司データを合わせてフラット化したもの(moon_to_flat(...)
)に対し、最後に気象データ(shimonoseki_df
)をマージしようとしているのですが,
というエラーが出てしまいました.
一方で,
ひとつ下のセル(31と書かれたもの)を見る限り,型は揃っているように見えます.
何が原因なのでしょうか. よろしくお願いいたします.
@tottocoslowlifer ご対応ありがとうございます. 以下コメントになります.
いくつかポイントがあります.
flat_merge()
関数と moon_to_flat()
関数を分けないまず, flat_merge()
関数の引数に shimonoseki_df
を使う必要はないと思います. 使い方としては, 関数内1行目〜6行目で shimonoseki_df
の 年月日時
をデータとしてまとめていることかと思われますが,
meta_append = []
for i in range(len(shimonoseki_df)):
ymd = shimonoseki_df.loc[i,"年月日時"]
if dt.datetime.date(ymd) in list(df["年月日"]):
meta_append.append([ymd])
この中にもいくつか注意事項があります.
if dt.datetime.date(ymd) in list(df["年月日"]):
の1行の中に df
があるのは NGmeta_append = [[shimonoseki_df.loc[i,"年月日時"]] for i in range(len(shimonoseki_df)) if dt.datetime.date(shimonoseki_df.loc[i,"年月日時"]) in list(merged_df["年月日"])]
の1行で書ける
meta_append
は要らないという感じです.
続いて, 一旦 moon_to_flat()
関数の話をします. こちらは手順として flat_merge()
関数と地続きなので, 関数を分ける必要はないと思います. また, how
については, 1
or 2
というのはあまり良くないです. 理由としては, それ以外を指定してしまった場合の処理が考慮されていないからです. ここは, 差分を取る場合は "diff"
とし, それ以外はそのまま入れるというような感じにしましょう.
ここまでを受けて, 一つの関数 flatten()
を作成する手順を提案します.
flatten()
関数作成に向けて3つに分けて紹介します.
["年月日", "潮位"]
に関して["こよみ", "気象庁", "MIRC"]
に関して["黄経差", "月齢"]
に関して["年月日", "潮位"]
に関してまず, merged_df
は,
のようになっているので, "年月日"
のカラムと "n時の潮高値(cm)"
と書かれているカラムを利用します.
time_cols = [f"{i}時の潮高値(cm)" for i in range(24)]
times = []
data = []
まずこのように初期状態を用意します. ここに, merged_df
の長さ分の for 文を使って append していきます.
for i in range(len(merged_df)):
times += [dt.datetime.combine(merged_df["年月日"][i], dt.time(time)) for time in range(24)]
data += merged_df[time_cols].iloc[i].to_list()
これで出来上がりです. 以下にまとめてみたので, 一旦どのような DataFrame が出来ているか確認してみましょう.
time_cols = [f"{i}時の潮高値(cm)" for i in range(24)]
times = []
data = []
for i in range(len(merged_df)):
times += [dt.datetime.combine(merged_df["年月日"][i], dt.time(time)) for time in range(24)]
data += merged_df[time_cols].iloc[i].to_list()
pd.DataFrame({"年月日時": times, "潮位": data})
このようになるかと思われます.
["こよみ", "気象庁", "MIRC"]
に関してこちらは特に時間毎の値を変更する必要はないので, そのまま引き伸ばすようにしましょう.
label_cols = ["こよみ", "気象庁", "MIRC"]
labels = []
for col in label_cols:
tmp = []
for i in range(len(merged_df)):
tmp += [merged_df[col][i] for _ in range(24)]
labels.append(tmp)
pd.DataFrame({"こよみ": labels[0], "気象庁": labels[1], "MIRC": labels[2]})
["黄経差", "月齢"]
に関してここは, how
という引数を使って分岐を入れます. まず, 黄経差と月齢のデータリストを表す変数 emd
, mp
を定義します.
emd = []
mp = []
続いて, how="diff"
以外はデータを引き伸ばすようにするので, if 文の処理を how="diff"
の場合に, else 文の中をそのままの場合にします.
how="diff"
の場合,
for i in range(len(merged_df)):
if i == len(merged_df) - 1:
emd_diff = (merged_df["黄経差"][i] - merged_df["黄経差"][i - 1]) / 24
mp_diff = (merged_df["月齢"][i] - merged_df["月齢"][i - 1]) / 24
else:
emd_diff = (merged_df["黄経差"][i + 1] - merged_df["黄経差"][i]) / 24
mp_diff = (merged_df["月齢"][i + 1] - merged_df["月齢"][i]) / 24
emd += [merged_df["黄経差"][i] + emd_diff * j for j in range(24)]
mp += [merged_df["月齢"][i] + mp_diff * j for j in range(24)]
という処理をします. この for 文内の if 文の分岐では, 最終日については前日との差をそのまま使うようにしています.
それ以外の場合,
for i in range(len(merged_df)):
emd += [merged_df["黄経差"][i] for _ in range(24)]
mp += [merged_df["月齢"][i] for _ in range(24)]
で良いと思います. まとめると,
how = "diff"
emd = []
mp = []
if how == "diff":
for i in range(len(merged_df)):
if i == len(merged_df) - 1:
emd_diff = (merged_df["黄経差"][i] - merged_df["黄経差"][i - 1]) / 24
mp_diff = (merged_df["月齢"][i] - merged_df["月齢"][i - 1]) / 24
else:
emd_diff = (merged_df["黄経差"][i + 1] - merged_df["黄経差"][i]) / 24
mp_diff = (merged_df["月齢"][i + 1] - merged_df["月齢"][i]) / 24
emd += [merged_df["黄経差"][i] + emd_diff * j for j in range(24)]
mp += [merged_df["月齢"][i] + mp_diff * j for j in range(24)]
else:
for i in range(len(merged_df)):
emd += [merged_df["黄経差"][i] for _ in range(24)]
mp += [merged_df["月齢"][i] for _ in range(24)]
pd.DataFrame({"黄経差": emd, "月齢": mp})
というようになると思います.
flatten()
関数今までのをまとめます.
def flatten(df: pd.DataFrame, how: str="same") -> pd.DataFrame:
time_cols = [f"{i}時の潮高値(cm)" for i in range(24)]
label_cols = ["こよみ", "気象庁", "MIRC"]
times = []
data = []
labels = []
emd = []
mp = []
for i in range(len(df)):
times += [dt.datetime.combine(df["年月日"][i], dt.time(time)) for time in range(24)]
data += df[time_cols].iloc[i].to_list()
for col in label_cols:
tmp = []
for i in range(len(df)):
tmp += [df[col][i] for _ in range(24)]
labels.append(tmp)
if how == "diff":
for i in range(len(df)):
if i == len(df) - 1:
emd_diff = (df["黄経差"][i] - df["黄経差"][i - 1]) / 24
mp_diff = (df["月齢"][i] - df["月齢"][i - 1]) / 24
else:
emd_diff = (df["黄経差"][i + 1] - df["黄経差"][i]) / 24
mp_diff = (df["月齢"][i + 1] - df["月齢"][i]) / 24
emd += [df["黄経差"][i] + emd_diff * j for j in range(24)]
mp += [df["月齢"][i] + mp_diff * j for j in range(24)]
else:
for i in range(len(df)):
emd += [df["黄経差"][i] for _ in range(24)]
mp += [df["月齢"][i] for _ in range(24)]
return pd.DataFrame(
{
"年月日時": times,
"潮位": data,
"黄経差": emd,
"月齢": mp,
"こよみ": labels[0],
"気象庁": labels[1],
"MIRC": labels[2],
}
)
そして, このようにします.
df = call_moji_tide()
moon_df = call_mooncal()
shimonoseki_df = call_shimonoseki()
merged_df = pd.merge(df, moon_df, on="年月日", how="inner")
flattened_df_same = flatten(merged_df)
flattened_df_diff = flatten(merged_df, how="diff")
print(type(merged_df["年月日"][0]))
これを見ると,
<class 'datetime.date'>
となっているようですね. また,
print(type(shimonoseki_df["年月日時"][0]))
を見ると,
<class 'datetime.datetime'>
のようになっています. すなわち, 門司のデータと月齢データをマージした時点では, merged_df["年月日"]
のデータ及び shimonoseki_df["年月日時"]
のデータの型は <class 'pandas._libs.tslibs.timestamps.Timestamp'>
ではなく, datetime
のクラスになっているようですね. つまり, Timestamp 型というのは間違いだったようです.
また,
ひとつ下のセル(31と書かれたもの)を見る限り,型は揃っているように見えます.
こちらについては,
shimonoseki_df["年月日時"]
を実行すると,
と出てきており, dtype: object
になっています. 一方,
moon_to_flat(2,meta_df,merged_df)["年月日時"]
を実行すると,
なので dtype: datetime64[ns]
と気象データとは異なりますね. 型が完全に一致しなかったということですね.
上のコメントを施せばおそらくマージできるようになると思いますが, flatten()
関数を作らない場合はまた別途相談しましょう.
ありがとうございます. コメントアウトを加えた上で再度コミットいたしましたので,ご確認の程よろしくお願いいたします.
確認できました. ご対応ありがとうございます.
それでは, flattened_df_same
で良いので, shimonoseki_df
とマージしてみましょう.
preprocessed_df = pd.merge(flattened_df_same, shimonoseki_df, on="年月日時", how="inner")
preprocessed_df
これを実行し, エラーが出ないかを確認しましょう. これができたら重回帰分析にかけてみましょう.
X: ["黄経差", "月齢", "こよみ", "気象庁", "MIRC", "降水量(mm)", "気温(℃)"] y: ["潮位"]
この際, ["潮位", "降水量(mm)", "気温(℃)"]
には欠損値が含まれていることに注意しましょう.
先ほどと同じエラーが出てしまいました.
@tottocoslowlifer
原因を調査してみました. どうやら, call_shimonoseki()
関数内の
for i in range(len(df)):
time = df.loc[i, "年月日時"]
dt_ymd = dt.datetime.strptime(time.split("日")[0]+"日", "%Y年%m月%d日")
dt_time = time.split("日")[1]
if dt_time == "24時":
dt_ymd = dt.date(dt_ymd.year, dt_ymd.month, dt_ymd.day) + dt.timedelta(days=1)
dt_time = "0時"
dt_time = dt.datetime.strptime(dt_time, "%H時")
dt_ymd = dt.date(dt_ymd.year, dt_ymd.month, dt_ymd.day)
dt_time = dt.time(dt_time.hour)
df.loc[i, "年月日時"] = dt.datetime.combine(dt_ymd, dt_time)
の最後の行で, 直接値を書き換えていたのが良くなかったみたいです. 例えば, for 文の前に
times = []
というのを用意し, 上記最後の行を
times.append(dt.datetime.combine(dt_ymd, dt_time))
のようにして一旦リストとして持っておいて, 後で DataFrame にするというのが良いかなと思います. なるべく call_shimonoseki()
関数の原型を崩さないように, かつフォーマットを揃えるように次のように書き換えてみました.
def call_shimonoseki() -> pd.DataFrame:
df = pd.read_csv("../data/Shimonoseki_2011-2021/Shimonoseki.csv").rename(columns={"Unnamed: 0": "年月日時"})
times = []
for i in range(len(df)):
time = df.loc[i, "年月日時"]
dt_ymd = dt.datetime.strptime(time.split("日")[0]+"日", "%Y年%m月%d日")
dt_time = time.split("日")[1]
if dt_time == "24時":
dt_ymd = dt.date(dt_ymd.year, dt_ymd.month, dt_ymd.day) + dt.timedelta(days=1)
dt_time = "0時"
dt_time = dt.datetime.strptime(dt_time, "%H時")
dt_ymd = dt.date(dt_ymd.year, dt_ymd.month, dt_ymd.day)
dt_time = dt.time(dt_time.hour)
times.append(dt.datetime.combine(dt_ymd, dt_time))
rain = []
temp = []
item_dict = {"降水量(mm)": rain, "気温(℃)": temp}
for i in range(len(df)):
for item in ["降水量(mm)", "気温(℃)"]:
if df.loc[i, item] == "--":
item_dict[item].append(float(0))
elif df.loc[i, item] == "///":
item_dict[item].append(float("nan"))
else:
df.loc[i, item] = "".join(re.findall(r"\d+\.\d+", str(df.loc[i, item])))
if df.loc[i, item] == "":
df.loc[i, item] =- float("nan")
item_dict[item].append(float(df.loc[i, item]))
return pd.DataFrame({"年月日時": times, "降水量(mm)": rain, "気温(℃)": temp})
書き方に質問があればお答えします. もし納得いただけたら, 同様にして call_mooncal()
関数も直してみましょう.
これでしっかりとデータの結合が出来るはずです. よろしくお願いいたします.
できました!!!
お疲れ様です.
call_mooncal()
関数の中で, ["こよみ", "気象庁", "MIRC"]
のラベリングが抜けてしまっているようですので, そちらもご対応よろしくお願い致します.
引き続き, こちらの重回帰分析にかけるところまでをよろしくお願い致します.
お疲れ様です.
という方向性で進めております. セル31で, 上の2を実装したつもりなのですが,セル32のコメントアウトの箇所を戻すと,以下のようなエラーが出ます.
欠損値を含むデータを取り除ききれていないということなのでしょうか. よろしくお願いいたします.
ありがとうございます. いくつかコメントがあります.
非常に細かいことで申し訳ありません. 各所の黄経差を表す単語, "longtitude" $\rightarrow$ "longitude" に直しましょう.
ここは消してしまいましょう.
このままで大丈夫なのですが, "date"
の情報は DataFrame どうしのマージの際に必要だったものなので,
preprocessed_df_diff = pd.merge(flattened_df_diff, shimonoseki_df, on="date", how="inner").drop("date", axis=1)
のようにして削除してしまいましょう.
こちらは「欠損値補完」の「メタ情報を用いた補完」の中に入っている項目なので, 「#」は4つにしましょう.
欠損値の確認は大事ですね◎
ヒストグラムを出していただいていますが, 意図があまりわかりませんでした. ここから相関係数の算出や, PCA(主成分分析)などをして特徴量の重要度を割り出すなどをしないのであれば不要な気もします.
いただいた質問ですが, なぜ2015〜2016年の欠損値が無いデータで学習する際にエラーが起きるかというと, この期間の中に "rainfall(mm)"
, temperature(℃)
の欠損値があるからです. これらを削除しない限りは上手くいきません. また, 欠損値は落としてから train_test_split()
をしましょう.
訓練データには欠損値を含まない期間のものを使用する
こちらですが, 基本的にデータは使えるだけ使いましょう. なぜなら, 今の欠損値補完は時系列データとしてでなく, テーブルデータとして見ているからです. 時間依存が無いので, より多くのデータを用いることができます.
手順としては,
"rainfall(mm)"
, temperature(℃)
の欠損が見られるデータを全て削除する"tide level"
の欠損が見られるデータを全て削除するX
, y
を作る
X = preprocessed_df_same[["longtitude", "moon phase", "calendar", "JMA", "MIRC", "rainfall(mm)", "temperature(℃)"]]
y = preprocessed_df_same["tide level"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.50, random_state=8192)
regressor = LinearRegression().fit(X_train, y_train)
train_test_split()
の random_state
を好きな値で構いませんので設定しましょう(shuffle=False
は要らないです).
ありがとうございます. コミットができましたので,ご確認の程よろしくお願いいたします.
@tottocoslowlifer ご対応ありがとうございます. 非常に綺麗なコーディングです◎ 2点コメントがあります.
X=
の部分に空白を入れるこれを直して次の作業をしていただきます.
作ったモデル regressor
で X_train
を使って predict()
し, y_train
との誤差を算出しましょう. 誤差については平均絶対誤差と平均二乗誤差を使ってみましょう.
また, ここまでを関数化してみましょう. train_predict()
などとし, 引数を X
と y
にして, 関数内で2種類の誤差について print()
し, 返り値にモデルを持ってきましょう. すなわち, return regressor
とします.
予測値についてはそれぞれ四捨五入で整数値に変換してから誤差を計算しましょう.
ありがとうございます. 再度コミットいたしました.
@tottocoslowlifer ありがとうございます. コードが簡潔で素晴らしいです◎ すみません, 追加で少し補正したいことがあります.
train_predict()
関数の引数に, split_rate
というのを入れ, train_test_split()
の test_size
を自由に変えられるようにするy_train_pred = regressor.predict(X_train)
の下に y_train_pred
の各要素を整数値に四捨五入する処理を追加するregressor = train_predict(X, y, split_rate)
とするこれが終わったら, 今までのことについて質問があればお答えします.
完了いたしました. 質問に関しては,その都度,聞かせていただいていたので,今は特にありません. ありがとうございます.
@tottocoslowlifer ありがとうございます. 最後に1点のみ, 忘れていたことがありましたのでご対応のほどよろしくお願い致します.
train_predict()
関数で, train
データを使わずに test
データにする次のように変えれば大丈夫です.
def train_predict(X, y, split_rate):
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=split_rate, random_state=316)
regressor = LinearRegression().fit(X_train, y_train)
y_pred = regressor.predict(X_test)
for i in range(len(y_pred)):
y_pred[i] = round(y_pred[i])
mae = np.mean(abs(y_pred - y_test))
mse = np.mean((y_pred - y_test) ** 2)
print("MAE train: ", mae)
print("MSE train: ", mse)
return regressor
ありがとうございます.コミットいたしました. 一点だけ質問なのですが,重回帰分析の前に 標準化を行うべきケースと行わなくてもよいケース の違いがわからずにいます.
「説明変数の数字のスケールが異なる場合は標準化を行う」という認識でいたのですが,今回の場合なら必要ないのでしょうか.
よろしくお願いいたします.
ご対応ありがとうございました.
一点だけ質問なのですが,重回帰分析の前に 標準化を行うべきケースと行わなくてもよいケース の違いがわからずにいます.
非常に良い質問だと思います. こちらについては, 「重回帰分析『だから』標準化が要らない」と回答させていただきます. 他の手法, 例えばニューラルネットワークなどであれば, そのスケールの違いから学習が困難になる場合が多いのですが, 重回帰分析では,
$$ y = \boldsymbol{a} \cdot \boldsymbol{X}, \quad \boldsymbol{a} = \left( a_1, a_2, \cdots, a_n \right), \quad \boldsymbol{X} = \left( x_1, x2, \cdots, x{n-1}, 1 \right) $$
と表され, 偏回帰係数 $\boldsymbol{a}$ を学習します. このとき, $a_n$ は切片を表します. 仮に $x_i$ についてスケーリングするとしましょう. $std_i$ をこの $x_i$ についての標準偏差, $\bar{x_i}$ を $x_i$ についての平均とすると, 標準化の式は
$$ standard_i = \frac{x_i - \bar{x_i}}{std_i} $$
となります. ここで, $standard_i$ が新しい $x_i$ の代わりになるのですが, 一方で
$$ x_i = standard_i \times std_i + \bar{x_i} $$
と表せ, $x_i$ の標準偏差及び平均は標本空間内では定数と見做せるため, ここに $a_i$ をかけると
$$ a_i \times x_i = a_i \times \left( standard_i \times std_i + \bar{x_i} \right) = \left( a_i \times std_i \right) \times standard_i + a_i \times \bar{x_i} $$
となり, 式全体への寄与としては, $a_i$ に定数 $std_i$ をかけ, $a_n$ に $a_i \times \bar{x_i}$ を足すだけになります. つまり, ただ式を線形変換しただけなので, 結果の予測値については影響を及ぼさないということです. これが重回帰分析の強みであり, 今回の欠損値補完のために手法として選んだ理由になります.
なるほど,重回帰分析の場合は,標準化の操作がスケールへ何ら影響しないということであっていますでしょうか.
ご回答ありがとうございました.
重回帰分析の場合は,標準化の操作がスケールへ何ら影響しないということであっていますでしょうか.
標準化をすることはデータの前処理としての過程であり, 特徴量のスケールを変更しているので, この考え方は少し違うように感じます. 「標準化をする」 = 「特徴量のスケールを揃える」です. 一方で, 重回帰分析による予測の際は, このスケールの「違い」についての影響を受けないということです.
ありがとうございます. 表現が不適切でした. 標準化の操作によりスケールを変更することが,結果に影響しないということでしょうか?
標準化の操作によりスケールを変更することが,結果に影響しないということでしょうか?
そうですね, それは正しいと思います.
分かりました.ありがとうございます.
他の事柄に関しては,現時点では質問がないです. よろしくお願いいたします.
ありがとうございます.
それでは次の話に参りたいと思いますが, 今回の重回帰分析の結果を見て「思ったほど良くない」と感じていただけると嬉しいです. では, 本当にこの方法でモデルを作ったことが良くなかったのかを考察してみましょう.
まず, 「メタ情報を用いた補完」では, 3種類の DataFrame の情報を用いてそれらを結合し, 不適切なデータを取り除いて, 次のように説明変数と目的変数を設定しました.
X
: ["longitude", "moon phase", "calendar", "JMA", "MIRC", "rainfall(mm)", "temperature(℃)"]
y
: ["tide level"]
この X
と y
を重回帰分析で学習させました. しかし, よく考えると説明変数の中には目的変数の値に直接影響させるファクターはなく, 補助的なものが多く含まれているだけといった感じです.
説明変数の中に, 過去(例えば直近10時間)の潮位のデータを特徴量として加えてみるということを考えます. 具体的には, preprocessed_df
を作った後, "tide level"
を1時間ずつずらしたカラムを用意し(上の例だと10個シフト), 欠損するデータを全て省いてから説明変数を次の n + 7 個にします.
X
: ["tide level shift 1h", "tide level shift 2h", ..., "tide level shift nh", "longitude", "moon phase", "calendar", "JMA", "MIRC", "rainfall(mm)", "temperature(℃)"]
preprocessed_df_same
, preprocessed_df_diff
を作る(26, 27個目のセルをまとめて次のようにすればOK)
preprocessed_df_same = pd.merge(flattened_df_same, shimonoseki_df, on="date", how="inner").drop("date", axis=1)
preprocessed_df_diff = pd.merge(flattened_df_diff, shimonoseki_df, on="date", how="inner").drop("date", axis=1)
"tide level"
を基準に n 時間シフトさせて n 個のカラムを生成する関数を作成する(DataFrame の .shift()
メソッドが使える)train_predict()
関数にかける先ほどの, train_predict()
関数の print()
部分にある train
の文字を削除していただきたいです.
print("MAE: ", mae)
print("MSE: ", mse)
対応が完了いたしました. ご確認の程よろしくお願いいたします.
@tottocoslowlifer ありがとうございます. 以下コメントになります.
精度も非常に高くなっていて, 処理としては概ね正しいです. 一方で最後のところで, 今まで使っていた降水量等のデータがなくなっているので, コード面でいくつか直していただきたいです. ご対応をよろしくお願い致します.
tide_shift()
関数について, 以下の変更を加えてみましょう.
tide_shift(df: pd.DataFrame, num: int)
とする=
だとコピー後のものに変更を加えると元の DataFrame にも影響が出るため, ここでは意味をなさない, 代入操作が必要であれば, pre_df = df.copy()
とするfor i in range(num):
pre_df = pre_df.dropna(subset=[f"tide level shift {i+1}h"])
ここは, せっかく X
を用意したので
pre_df = pre_df.dropna(subset=X)
とする
return pre_df, X
とする(リストにする必要はない)次のようにしましょう.
preprocessed_df_same_shift, new_cols = tide_shift(preprocessed_df_same, 10)
cols = ["longitude", "moon phase", "calendar", "JMA", "MIRC", "rainfall(mm)", "temperature(℃)"] + new_cols
X = preprocessed_df_same_shift[cols]
y = preprocessed_df_same_shift["tide level"]
regressor = train_predict(X, y, 0.50)
夜分に申し訳ありません. 対応が完了いたしました.
@tottocoslowlifer ご対応ありがとうございました.
それではこの issue で最後のステップになりますが, 上で作った regressor
を使って, 元の潮位のデータを全て補完してみましょう.
お疲れ様です. 方向性を全く理解できていない状態なので,補完の操作の流れを示していただけると幸いです. 申し訳ありません.よろしくお願いいたします.
方向性を全く理解できていない状態なので
こちらは,
でいうとどちらでしょうか?
1です.
ありがとうございます.
まず, 機械学習というものは, 次のような分析方法です(出典: 機械学習 | 用語解説 | 野村総合研究所(NRI)).
データを分析する方法の1つで, データから「機械」が自動で「学習」し, データの背景にあるルールやパターンを発見する方法. 近年では, 学習した成果に基づいて「予測・判断」することが重視されるようになった.
機械学習や深層学習などの本質は, 学習させること自体ではなく, そこから機械が導いたロジックに基づいて「予測・判断」をすることにあります. 今回の場合, なぜ重回帰分析をしたのかを考えていただきたいです. 重回帰分析は, 教師あり学習の「回帰」問題で扱われる代表的なモデル構造であり, 説明変数から目的変数を予測する機械学習アルゴリズムです. 今までの「メタ情報を用いた補完」では, いくつかの説明変数を用意し, それに対する線形写像である(ことが望ましい)目的変数を予測するということを重回帰分析を用いて行いました.
説明変数:
X(ある日のある時刻における) = [
"黄経差", "月齢", "こよみ", "気象庁",
"MIRC", "降水量", "気温",
"1時間前の潮位", "2時間前の潮位", "3時間前の潮位",
"4時間前の潮位", "5時間前の潮位", "6時間前の潮位",
"7時間前の潮位", "8時間前の潮位", "9時間前の潮位", "10時間前の潮位",
]
目的変数:
y(ある日のある時刻における) = ["潮位"]
さて, これである日のある時刻の説明変数が揃っている場合, これらの情報を基にその時刻の潮位を機械が教えてくれるようになりました. これが現在の状況です.
今の notebook の一番最後のセルの一番最後の regressor
という変数は, 平均絶対誤差が 2.80
, 平均二乗誤差が 13.11
でこの潮位を予測できるモデルとなっています. すなわち, 説明変数分のデータがあれば欠損された潮位のデータを, 本来の値から3前後だけしかズレないで予測してくれるということです. 元の潮位のデータは28個目のセルより97個欠損していることが分かります. これら97個の時刻における説明変数のデータを用いてこのモデルにかけることで, 欠損されたデータを補うことができるということになります.
コーディングに移る前に, 今一度やってきたことでわからないことが無いかを確認いただき, 上記の説明に納得していただきたく存じます.
ありがとうございます.
ある日のある時刻の説明変数が揃っている場合, これらの情報を基にその時刻の潮位を機械が教えてくれる
とあるのですが,欠損値を含むデータにregressor
を用いる際,
例えば $y$ にあたる潮位データが欠けていて, さらにその行の説明変数にあたるデータも欠損値を含む
というケースはないのでしょうか.(1時間前の潮位もnan
であるケース,降水量もnan
であるケースなど)
それとも,処理の上で
pre_df = pre_df.dropna(subset=["rainfall(mm)", "temperature(℃)"])
pre_df = pre_df.dropna(subset=X)
としてこれらのケースを除くのでしょうか.
よろしくお願いいたします.
ありがとうございます. 良いご質問です.
結論から言うと, それはご自身で確認いただきたいところです. 潮位のデータとその他のデータが同時に欠損している場合が無いかを調べ, 無ければそのまま regressor
に入れる, ある場合はもう一度重回帰分析のモデルを作り直すのが良いでしょう. 今回の「欠損値補完」のセクションでは, 「統計的情報を用いた補完」を含めて欠損値を補完することが目的です.
お疲れ様です.
調べてみた結果,tide level
のデータが連続して欠損している箇所が複数,ありました.
そのため,これらの箇所周辺で"tide level shift nh"
が欠損値ばかりになり,説明変数として機能しないような気がします.
重回帰分析モデルの説明変数を再度,見直すべきでしょうか.
潮位のデータのみが連続して欠損している場合は, 一つずつ補ってずらすというように再帰的にモデルに入れてみると良いと思います. モデルの説明変数を見直す必要はありません.
ありがとうございます. 取り組んでみたのですが行き詰まってしまいました.
お手数ですが,ZOOMミーティングを設定させていただけたら幸いです. よろしくお願いいたします.
承知致しました. それではまたカレンダー内の「空」の部分に予定を入れておいてください. Zoom の情報は以下の通りになります.
https://tus-ac-jp.zoom.us/j/92156768099?pwd=VU1MOFdiNFVkdzhBZlNTVXRRWnVpUT09
ミーティング ID: 921 5676 8099 パスコード: 302739
よろしくお願い致します.
欠損値の補完が完了したため close とします. お疲れ様でした.
こちらより
欠損値補完の処理として, メタ情報を考慮した補完を行いたい.
2011年1月1日〜2021年12月31日までの11年間のデータを用いて, 以下のメタ情報を付加した重回帰分析によって補完する方法を考えてみる.
重回帰分析
重回帰分析は, 説明変数 $x_i (i = 1, ..., n)$ と目的変数 $y$ について, 偏回帰係数 $a_i (i = 0, 1, ..., n)$ をモデルの学習によって決定し,
$$y = a_0 + a_1x_1 + \cdots + a_nx_n$$
という回帰式を作ることを目的とする分析である.
復習資料:
今回は, 目的変数を時刻 $t$ の潮位に, 説明変数を時刻 $t$ の,
中潮
$\rightarrow$3
など )--
は0
に変換するの計7個とする.
対応していただくこと
まずは2種類のファイル
tide/data/Moon_2011-2021/Mooncal_2011-2021.csv
tide/data/Shimonoseki_2011-2021/Shimonoseki.csv
を呼び出し,
tide/data/Moji_Tide_2011-2021/csv/9010_2011-2021.csv
と上手く時刻毎に結合されたDataFrame
を作成してみましょう.