pPolis2024-10-29
>nishio 東大谷口研の2022年参院選でのデータからUMAPで可視化した、共産島(共産党の島)とかができてる
> 
> 
> 
> 





pyimport umap
# UMAPによる次元削減
reducer = umap.UMAP(
n_neighbors=15, min_dist=0.1, n_components=2, random_state=42, n_jobs=1
)
embedding = reducer.fit_transform(matrix)
# UMAPの結果を含むDataFrameを作成し、matrixのインデックス(User_ID)を含める
umap_df = pd.DataFrame(embedding, columns=["UMAP1", "UMAP2"])
umap_df["User_ID"] = matrix.index # matrixのインデックスをUser_IDとする
# dataにUser_ID列を追加してから、umap_dfとマージ
data = data.reset_index().rename(
columns={"index": "User_ID"}
) # User_IDとしてインデックスを追加
merged_data = pd.merge(data, umap_df, on="User_ID", how="inner") # User_IDで結合
# 政党名の列を追加
merged_data["PARTY_NAME"] = merged_data["PARTY"].map(political_parties)
data = merged_data
# カラーマップの準備
unique_parties = data["PARTY"].unique()
colors = plt.cm.tab10(np.linspace(0, 1, len(unique_parties)))
parties_letter = {
"自民党": "自",
"立憲民主党": "立",
"公明党": "公",
"日本維新の会": "維",
"国民民主党": "国",
"共産党": "共",
"社民党": "社",
"れいわ新選組": "れ",
"NHK党": "N",
"参政党": "参",
"諸派": "諸",
"無所属候補": "無",
}
# 可視化
plt.figure(figsize=(10, 7))
for i, party in enumerate(unique_parties):
party_data = data[data["PARTY"] == party]
pname = political_parties[party]
pletter = parties_letter[political_parties[party]]
plt.scatter(
party_data["UMAP1"],
party_data["UMAP2"],
s=0,
marker="+",
color=colors[i],
label=f"{pname}({pletter})",
)
for p in party_data.iterrows():
plt.text(
p[1]["UMAP1"],
p[1]["UMAP2"],
s=pletter,
color=colors[i],
fontsize=5,
ha="center",
va="center",
)
plt.title("Political Parties UMAP Visualization")
plt.xlabel("UMAP1")
plt.ylabel("UMAP2")
plt.legend(title="政党")
plt.show()
割とクラスタが分かれてくれるので、次はこれにPolis的クラスタ特徴抽出を掛けて、世論地図的なAIクラスタ解説をすると面白い
---
2022年参院選のPolis的可視化に関して、以前はPCAでやってて、先日その主成分軸をAIに解説させてあまり面白くなくて
それを踏まえて「じゃあ軸に意味が見出せなくてもいいから別の方法で可視化してもいいよね」とTalk to the Cityで使われてるUMAPを使ってみたものがこちら
なんかそれっぽく別れてますね
共産党だけで集まってる離れ小島の「過激な共産党」と他の政党と同居してる「マイルド共産党」がいて面白い
自民もそう
維新の離れ小島に混ざってる自民党の人は誰だw(もちろん調べればわかる)
UMAPしてからクラスタリングしてAIに解説させようかと思ってたけど、こんな感じに別れるならはっきり分かれてるのは人間が範囲指定してクラスタにしての大きい島だけクラスタリングで数個に分けた方が納得感が高いかも
---
文脈を補うと、600人でPCAしたら二分割にしかならなかったこのデータでもUMAPしたら6つにわかれたわけなので、世論地図の二分割になってるものもUMAPを使えば細かく分かれた詳細地図ができるかもねという予備実験でした
---
Scatterから範囲選択してどこが選択されたか表示
py# 選択範囲内のデータを表示する関数
def on_select(eclick, erelease):
x1, y1 = eclick.xdata, eclick.ydata # 選択範囲の開始点
x2, y2 = erelease.xdata, erelease.ydata # 選択範囲の終了点
# 選択範囲とデータを表示
print(
f"Selected range: x=[{min(x1, x2):.2f}, {max(x1, x2):.2f}], y=[{min(y1, y2):.2f}, {max(y1, y2):.2f}]"
)
from matplotlib.widgets import RectangleSelector
# RectangleSelectorを使用して選択イベントを監視
fig, ax = plt.subplots()
selector = RectangleSelector(ax, on_select, useblit=True, button=[1], interactive=True)
# 可視化
# plt.figure(figsize=(10, 7))
for i, party in enumerate(unique_parties):
party_data = data[data["PARTY"] == party]
pname = political_parties[party]
pletter = parties_letter[political_parties[party]]
ax.scatter(
party_data["UMAP1"],
party_data["UMAP2"],
s=0,
marker="+",
color=colors[i],
label=f"{pname}({pletter})",
)
for p in party_data.iterrows():
ax.text(
p[1]["UMAP1"],
p[1]["UMAP2"],
s=pletter,
color=colors[i],
fontsize=5,
ha="center",
va="center",
)
plt.title("Political Parties UMAP Visualization")
plt.xlabel("UMAP1")
plt.ylabel("UMAP2")
plt.legend(title="政党")
plt.show()
いま
Polis的分析(Polisのリアルタイム散布図を省いたもの)を一つのコードでやってるけど、部品が理解できてきたから分解していこう
クラスタの集合を与えられて、そのクラスタの中と外を分ける特徴的な意見をだす部品
図ではフィッシャー正確検定と描いているが正確な名称ではない
なのだけどそもそも技術的に同じものを使っているってだけの話で、部品の使用意図は全く異なってる
違うクラスタかどうかを検定しているのではなく、検定する時に使われる尺度をクラスタを特徴付ける質問を選ぶときの尺度に流用している
たぶんこの部分を部品として扱ってないから概念を表現するのに適切な言葉がまだない
この部品の部分を切り出して独立して使えるようにしたい
同じように世論地図のデータもUMAPして政党アイコンを出せば政党に代表されていない意見クラスタがわかるかもな〜
---
2024-10-31
>nishio 1枚目この範囲の議員が持ってる特徴的な意見ベクトルが2枚目
> 

>nishio AIが勝手にクラスタリングするだけではなく、人間が「この範囲」と指定して分析できるようになったって話
> 
このクラスタの中でさらに左上の純粋共産島と他の部分にどういう意見の差があるのか見たくなってきた
数学的にはできるはずだ...
技術的には...
挫折した(少なくともチョチョイとできることではなかった)
諦めて個別クラスタをやったら意外と目的が達成された
結果はここにおいた