#目的
ブクログには読書記録という機能から
- 月ごとにどんな本をどれ位読んだか(観たか)
- 月、年ごとにどの本を読んだか(観たか)
という時系列でどんな本読んだ~?みたいな情報を手軽に確認できる機能がある。
が、いつの間にか機能が簡略化されてしまったようで
2年前以上の詳細が参照できない。
月合計で何冊読んだ?くらいはわかるが、どの本読んだかがぱっとわからない。
無くても困らないっちゃ困らないのだが、
たまに昔の読書を振り返る時とかに不便、というか無いと寂しい。
ただ、ブクログは登録した本の一覧をcsvでexportできる。
ということはそのcsvで集計すればいっか。
csvをExcel読み込み→集計でもよいのだが
読書メモに集計には不要な情報を大量に突っ込んでおり
なんとなくデータの整形が面倒そうなので、pandasで集計してみる。
##集計用スクリプト
import pandas as pd
def agg_execute(filename):
csv_header = [
"サービスID",
"アイテムID",
"13桁ISBN",
"カテゴリ",
"評価",
"読書状況",
"レビュー",
"タグ",
"読書メモ(非公開)",
"登録日時",
"読了日",
"タイトル",
"作者名",
"出版社名",
"発行年",
"ジャンル",
"ページ数",
]
book_log_df = pd.read_csv(
filename,
names=csv_header,
encoding="shift_jis",
)
"""
データクリーニング
欲しいデータは読み終わった&読了日を設定しているデータ
"""
filter_df = book_log_df[
(book_log_df["読書状況"] == "読み終わった")
& (book_log_df["読了日"] != None)
& (book_log_df["読了日"] != "0000-00-00 00:00:00")
].copy()
# 読了日を時系列データとして設定
filter_df["読了日"] = pd.to_datetime(filter_df["読了日"])
filter_df = filter_df.set_index("読了日")
# 時系列のインデックスを設定
df_m = filter_df.set_index(
[filter_df.index.year, filter_df.index.month, filter_df.index]
)
df_m.index.names = ["year", "month", "読了日"]
# 月ごとのジャンル内訳
df_m_by_genre = (
df_m[["ジャンル"]].groupby(["year", "month", "ジャンル"]).size().sort_index()
)
df_m_by_genre.name = "冊"
df_m_by_genre.to_csv("result_count_by_genre_yyyymm.csv", encoding="utf-8")
# 月ごとのページ数合計
df_m_by_page = df_m["ページ数"].sum(level=("year", "month")).sort_index()
df_m_by_page.to_csv("result_page_sum.csv", encoding="utf-8")
# 月ごとのタイトル一覧
df_m_by_title = df_m["タイトル"].sum(level=("year", "month"))
df_m_by_title = df_m["タイトル"].groupby(level=("year", "month")).apply(list)
df_m_by_title.to_csv("result_title_sum.csv", encoding="utf-8")
if __name__ == "__main__":
# 読み込むcsvを指定
agg_execute("booklog.csv")
##実行結果
year,month,ジャンル,冊
2020,1,映画,3
2020,1,本,12
2020,2,映画,1
2020,2,本,16
2020,3,本,4
2020,4,映画,3
2020,5,映画,2
2020,5,本,15
2020,6,マンガ,1
2020,6,映画,2
2020,6,本,31
これが見たかっただけです、はい。
result_page_sum.csvでページ/month,
result_title_sum.csvで月ごとの読んだ本のタイトルをずらずら出力。
##メモ
filter_df = book_log_df[
(book_log_df["読書状況"] == "読み終わった")
& (book_log_df["読了日"] != None)
& (book_log_df["読了日"] != "0000-00-00 00:00:00")
]
最初こんな感じで書いてたら、
それっぽい結果は出るんだけれど
SettingWithCopyWarningという警告が出る。
filter_df = book_log_df[
(book_log_df["読書状況"] == "読み終わった")
& (book_log_df["読了日"] != None)
& (book_log_df["読了日"] != "0000-00-00 00:00:00")
].copy()
今回のケースではそのままでも結果に差異がなかったのだが、、
普通に=で代入しちゃうと元データに対する処理か、
新しく作成しようとしたデータに対しての処理なのかが
pandas側では判断できないので、ちゃんとcopyして渡すこと。
タイトルじゃなくてISBNのリスト残す方が、
今後別サービスとかに移行する時楽そうなので、
そっちも保存するようにしたほうがいいかも。
##thx
[pandas.DataFrame, Seriesを時系列データとして処理]
(https://note.nkmk.me/python-pandas-time-series-datetimeindex/)
[pandasで時系列データの曜日や月、四半期、年ごとの合計や平均を算出]
(https://note.nkmk.me/python-pandas-time-series-multiindex/)
[Pandasのgroupbyを使った要素をグループ化して処理をする方法]
(https://deepage.net/features/pandas-groupby.html)
[pandas の SettingWithCopyWarning で苦労した話]
(https://qiita.com/HEM_SP/items/56cd62a1c000d342bd70)
[pandasのgroupbyでグループ化した文字列を結合する]
(https://qiita.com/ground0state/items/2fb8d1938220df8a0194)