Claude 3.7 Sonnetのコーディング能力を試す 4
以前の投稿でClaude 3.7 Sonnetのコーディング能力を試した.今回は言語系統樹を試してみた.
プロンプトと結果は下記..(最後の保存のコードがコメントアウトされていたので,その部分のみ手動で修正した)
こちらも動作はしているが描画が上手くいっていない.
Pythonで言語の進化系統樹を作成.
最高級にリッチな図になるように工夫して.
エラーが出ないように完璧なコードを出して
import plotly.graph_objects as go
import plotly.express as px
import networkx as nx
import numpy as np
import json
import random
from plotly.subplots import make_subplots
# ランダムシード設定(再現性のため)
random.seed(42)
np.random.seed(42)
# 言語ファミリーのデータ定義
language_data = {
"Indo-European": {
"Germanic": {
"North": ["Icelandic", "Faroese", "Norwegian", "Danish", "Swedish"],
"West": {
"English": ["Old English", "Middle English", "Modern English"],
"Dutch": ["Dutch", "Afrikaans", "Flemish"],
"German": ["Old High German", "Middle High German", "Modern German", "Yiddish"],
"Frisian": ["West Frisian", "North Frisian", "Saterland Frisian"]
}
},
"Romance": {
"Western": ["Portuguese", "Spanish", "Catalan", "French", "Italian"],
"Eastern": ["Romanian", "Aromanian"]
},
"Slavic": {
"West": ["Polish", "Czech", "Slovak", "Sorbian"],
"East": ["Russian", "Ukrainian", "Belarusian"],
"South": ["Bulgarian", "Macedonian", "Serbo-Croatian", "Slovenian"]
},
"Indo-Iranian": {
"Indo-Aryan": ["Sanskrit", "Hindi", "Urdu", "Bengali", "Punjabi", "Gujarati", "Marathi", "Nepali"],
"Iranian": ["Persian", "Kurdish", "Pashto", "Tajik", "Ossetian"]
},
"Balto-Slavic": {
"Baltic": ["Lithuanian", "Latvian"]
},
"Celtic": {
"Insular": {
"Goidelic": ["Irish", "Scottish Gaelic", "Manx"],
"Brittonic": ["Welsh", "Breton", "Cornish"]
}
},
"Hellenic": ["Ancient Greek", "Modern Greek"],
"Albanian": ["Albanian"],
"Armenian": ["Classical Armenian", "Eastern Armenian", "Western Armenian"]
},
"Uralic": {
"Finno-Ugric": {
"Finnic": ["Finnish", "Estonian", "Karelian", "Veps"],
"Sami": ["Northern Sami", "Southern Sami", "Skolt Sami"],
"Ugric": ["Hungarian"]
},
"Samoyedic": ["Nenets", "Selkup"]
},
"Altaic": {
"Turkic": ["Turkish", "Azerbaijani", "Turkmen", "Uzbek", "Kazakh", "Kyrgyz", "Uyghur"],
"Mongolic": ["Mongolian", "Buryat", "Kalmyk"],
"Tungusic": ["Evenki", "Manchu"]
},
"Sino-Tibetan": {
"Sinitic": ["Mandarin", "Cantonese", "Wu", "Min", "Hakka"],
"Tibeto-Burman": ["Tibetan", "Burmese", "Nepali"]
},
"Japonic": ["Japanese", "Ryukyuan"],
"Koreanic": ["Korean"],
"Austronesian": {
"Malayo-Polynesian": ["Malay", "Indonesian", "Javanese", "Tagalog", "Hawaiian", "Māori", "Samoan"]
},
"Afro-Asiatic": {
"Semitic": ["Arabic", "Hebrew", "Amharic", "Tigrinya"],
"Berber": ["Tamazight", "Kabyle"],
"Egyptian": ["Ancient Egyptian", "Coptic"],
"Cushitic": ["Somali", "Oromo"]
},
"Niger-Congo": {
"Bantu": ["Swahili", "Zulu", "Xhosa", "Shona"],
"Mande": ["Bambara", "Mandinka"]
},
"Dravidian": ["Tamil", "Telugu", "Malayalam", "Kannada"]
}
# カラーマップの定義(言語ファミリーごとに色を割り当て)
family_colors = {
"Indo-European": "#1f77b4", # 青
"Uralic": "#ff7f0e", # オレンジ
"Altaic": "#2ca02c", # 緑
"Sino-Tibetan": "#d62728", # 赤
"Japonic": "#9467bd", # 紫
"Koreanic": "#8c564b", # 茶色
"Austronesian": "#e377c2", # ピンク
"Afro-Asiatic": "#7f7f7f", # グレー
"Niger-Congo": "#bcbd22", # 黄緑
"Dravidian": "#17becf" # 水色
}
# 言語の地理的な位置(おおよその中心位置、ヒートマップのため)
language_locations = {
"Indo-European": {"lat": 50, "lon": 20},
"Germanic": {"lat": 55, "lon": 10},
"North": {"lat": 60, "lon": 15},
"West": {"lat": 52, "lon": 5},
"English": {"lat": 53, "lon": -2},
"Dutch": {"lat": 52, "lon": 5},
"German": {"lat": 51, "lon": 10},
"Frisian": {"lat": 53, "lon": 7},
"Romance": {"lat": 42, "lon": 12},
"Western": {"lat": 41, "lon": 5},
"Eastern": {"lat": 45, "lon": 25},
"Slavic": {"lat": 50, "lon": 25},
"West": {"lat": 50, "lon": 19},
"East": {"lat": 55, "lon": 30},
"South": {"lat": 44, "lon": 20},
"Indo-Iranian": {"lat": 30, "lon": 70},
"Indo-Aryan": {"lat": 25, "lon": 77},
"Iranian": {"lat": 33, "lon": 55},
"Balto-Slavic": {"lat": 55, "lon": 25},
"Baltic": {"lat": 56, "lon": 24},
"Celtic": {"lat": 54, "lon": -5},
"Insular": {"lat": 54, "lon": -5},
"Goidelic": {"lat": 54, "lon": -7},
"Brittonic": {"lat": 52, "lon": -4},
"Hellenic": {"lat": 39, "lon": 22},
"Albanian": {"lat": 41, "lon": 20},
"Armenian": {"lat": 40, "lon": 44},
"Uralic": {"lat": 62, "lon": 30},
"Finno-Ugric": {"lat": 60, "lon": 25},
"Finnic": {"lat": 61, "lon": 25},
"Sami": {"lat": 68, "lon": 20},
"Ugric": {"lat": 47, "lon": 19},
"Samoyedic": {"lat": 67, "lon": 80},
"Altaic": {"lat": 45, "lon": 90},
"Turkic": {"lat": 40, "lon": 70},
"Mongolic": {"lat": 47, "lon": 105},
"Tungusic": {"lat": 55, "lon": 125},
"Sino-Tibetan": {"lat": 30, "lon": 100},
"Sinitic": {"lat": 35, "lon": 110},
"Tibeto-Burman": {"lat": 28, "lon": 90},
"Japonic": {"lat": 35, "lon": 135},
"Koreanic": {"lat": 37, "lon": 128},
"Austronesian": {"lat": 0, "lon": 120},
"Malayo-Polynesian": {"lat": -5, "lon": 120},
"Afro-Asiatic": {"lat": 15, "lon": 35},
"Semitic": {"lat": 25, "lon": 40},
"Berber": {"lat": 30, "lon": 0},
"Egyptian": {"lat": 26, "lon": 30},
"Cushitic": {"lat": 8, "lon": 40},
"Niger-Congo": {"lat": 5, "lon": 10},
"Bantu": {"lat": -10, "lon": 25},
"Mande": {"lat": 10, "lon": -5},
"Dravidian": {"lat": 13, "lon": 78}
}
# グラフデータ構造を作成
def build_graph(data, parent=None):
G = nx.DiGraph()
# 最初の親ノードがNoneの場合は言語ファミリーを追加
if parent is None:
for family, subfamilies in data.items():
G.add_node(family, level=0, color=family_colors.get(family, "#000000"))
subgraph = build_graph(subfamilies, family)
G = nx.compose(G, subgraph)
else:
parent_level = G.nodes[parent]["level"] if parent in G.nodes else 0
# 辞書型データと文字列型データを別々に処理
if isinstance(data, dict):
for subfamily, languages in data.items():
node_name = subfamily
G.add_node(node_name, level=parent_level+1,
color=family_colors.get(parent.split("/")[0] if "/" in parent else parent, "#cccccc"))
G.add_edge(parent, node_name)
subgraph = build_graph(languages, node_name)
G = nx.compose(G, subgraph)
elif isinstance(data, list):
for language in data:
node_name = language
G.add_node(node_name, level=parent_level+1,
color=family_colors.get(parent.split("/")[0] if "/" in parent else parent, "#cccccc"))
G.add_edge(parent, node_name)
else:
node_name = data
G.add_node(node_name, level=parent_level+1,
color=family_colors.get(parent.split("/")[0] if "/" in parent else parent, "#cccccc"))
G.add_edge(parent, node_name)
return G
# グラフを作成
G = build_graph(language_data)
# 階層的なレイアウトの作成(カスタム)
def hierarchical_layout(G, root_nodes=None, width=1.0, height=1.0, xcenter=0.5, ycenter=0.5):
pos = {}
if root_nodes is None:
# レベル0のノードをルートとして使用
root_nodes = [n for n, d in G.nodes(data=True) if d.get('level', 0) == 0]
# ノードをレベルごとにグループ化
nodes_by_level = {}
for node, data in G.nodes(data=True):
level = data.get('level', 0)
if level not in nodes_by_level:
nodes_by_level[level] = []
nodes_by_level[level].append(node)
# ルートノードの処理
num_roots = len(root_nodes)
for i, node in enumerate(root_nodes):
angle = 2 * np.pi * i / num_roots
x = xcenter + 0.05 * np.cos(angle)
y = ycenter + 0.05 * np.sin(angle)
pos[node] = (x, y)
max_level = max(nodes_by_level.keys())
# レベル1以降の処理
for level in range(1, max_level + 1):
if level not in nodes_by_level:
continue
nodes = nodes_by_level[level]
radius = 0.3 * level / max_level
# ノードを親ノードの周りに配置
for node in nodes:
parents = list(G.predecessors(node))
if not parents:
continue
parent = parents[0] # 単一の親を仮定
# 同じ親を持つ兄弟ノードを探す
siblings = [n for n in G.successors(parent)]
num_siblings = len(siblings)
idx = siblings.index(node)
# 親ノードの座標
px, py = pos[parent]
# 角度を計算(兄弟の間で均等に分配)
angle = 2 * np.pi * idx / num_siblings
# 親ノードを中心とした座標
x = px + radius * np.cos(angle)
y = py + radius * np.sin(angle)
# 座標範囲内に収まるように調整
x = max(0, min(1, x))
y = max(0, min(1, y))
pos[node] = (x, y)
return pos
# 円形レイアウトの作成
pos = hierarchical_layout(G)
# ノードの色とサイズを設定
node_colors = [G.nodes[node].get('color', '#1f77b4') for node in G.nodes()]
node_sizes = [20 if G.nodes[node].get('level', 0) > 1 else 25 if G.nodes[node].get('level', 0) == 1 else 30 for node in G.nodes()]
# エッジのトレースを作成
edge_trace = []
for edge in G.edges():
x0, y0 = pos[edge[0]]
x1, y1 = pos[edge[1]]
# 親ノードの色を取得
parent_color = G.nodes[edge[0]].get('color', '#888')
# エッジを曲線として描画するための中間点を計算
# 直線ではなく、少し湾曲させる
xmid = (x0 + x1) / 2
ymid = (y0 + y1) / 2
# 湾曲の度合いをランダムに調整
curve_strength = 0.05 + 0.03 * random.random()
# 湾曲方向をランダムに決定
curve_direction = 1 if random.random() > 0.5 else -1
# 中間点を少しずらす
xmid += curve_direction * curve_strength * (y1 - y0)
ymid -= curve_direction * curve_strength * (x1 - x0)
# ベジェ曲線のためのより多くの中間点
t_values = np.linspace(0, 1, 50)
x_bezier = [(1-t)**2 * x0 + 2*(1-t)*t*xmid + t**2 * x1 for t in t_values]
y_bezier = [(1-t)**2 * y0 + 2*(1-t)*t*ymid + t**2 * y1 for t in t_values]
# エッジのトレースを追加
edge_trace.append(go.Scatter(
x=x_bezier, y=y_bezier,
line=dict(width=1.5, color=parent_color, shape='spline'),
hoverinfo='none',
mode='lines',
showlegend=False
))
# ノードのトレースを作成
node_trace = go.Scatter(
x=[pos[node][0] for node in G.nodes()],
y=[pos[node][1] for node in G.nodes()],
mode='markers+text',
text=[node for node in G.nodes()],
textposition="middle right",
textfont=dict(size=[8 if G.nodes[node].get('level', 0) > 1 else 10 if G.nodes[node].get('level', 0) == 1 else 14 for node in G.nodes()],
color=['#000000' if G.nodes[node].get('level', 0) > 0 else '#ffffff' for node in G.nodes()]),
marker=dict(
showscale=False,
color=node_colors,
size=node_sizes,
line=dict(width=2, color='#ffffff')
),
hovertemplate='%{text}<extra></extra>'
)
# 言語ファミリーの凡例を作成
legend_trace = []
for family, color in family_colors.items():
legend_trace.append(go.Scatter(
x=[None],
y=[None],
mode='markers',
marker=dict(size=10, color=color),
name=family,
hoverinfo='name'
))
# フィギュアの作成
fig = make_subplots(rows=1, cols=1, specs=[[{'type': 'scatter'}]])
# エッジを追加
for trace in edge_trace:
fig.add_trace(trace)
# ノードを追加
fig.add_trace(node_trace)
# 凡例を追加
for trace in legend_trace:
fig.add_trace(trace)
# レイアウトの設定
fig.update_layout(
title={
'text': '<b>世界言語の進化系統図</b>',
'font': {'size': 24, 'color': '#333333', 'family': 'Arial, sans-serif'},
'x': 0.5,
'xanchor': 'center'
},
plot_bgcolor='rgba(240, 240, 240, 0.8)',
paper_bgcolor='rgba(250, 250, 250, 0.9)',
width=1200,
height=900,
margin=dict(t=100, b=20, l=20, r=20),
hovermode='closest',
showlegend=True,
legend=dict(
title='言語ファミリー',
font=dict(size=12),
bordercolor='#999999',
borderwidth=1,
itemsizing='constant'
),
annotations=[
dict(
text='<b>言語進化系統樹</b> - 地球上の主要言語ファミリーとその関係性',
showarrow=False,
xref='paper',
yref='paper',
x=0.5,
y=-0.05,
font=dict(size=14)
)
]
)
# 軸の設定
fig.update_xaxes(showgrid=False, zeroline=False, showticklabels=False)
fig.update_yaxes(showgrid=False, zeroline=False, showticklabels=False)
# 3D的な見た目のための影の追加
for node in G.nodes():
x, y = pos[node]
shadow_size = G.nodes[node].get('level', 0)
shadow_color = 'rgba(50,50,50,0.1)'
fig.add_trace(go.Scatter(
x=[x],
y=[y - 0.003],
mode='markers',
marker=dict(
color=shadow_color,
size=node_sizes[list(G.nodes()).index(node)] * 0.9,
opacity=0.5
),
hoverinfo='none',
showlegend=False
))
# 世界地図のアウトラインを追加(装飾目的)
world_lats = [-60, -60, 70, 70, -60]
world_lons = [-150, 170, 170, -150, -150]
fig.add_trace(go.Scatter(
x=[0.1 + 0.8 * (lon - min(world_lons)) / (max(world_lons) - min(world_lons)) for lon in world_lons],
y=[0.1 + 0.8 * (lat - min(world_lats)) / (max(world_lats) - min(world_lats)) for lat in world_lats],
mode='lines',
line=dict(color='rgba(100,100,100,0.2)', width=1),
fill='toself',
fillcolor='rgba(200,200,200,0.1)',
hoverinfo='none',
showlegend=False
))
# 装飾的な要素の追加
# 背景にグラデーションを追加
fig.add_layout_image(
dict(
source="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFeAJ5HxO1KgAAAABJRU5ErkJggg==",
xref="paper", yref="paper",
x=0, y=1,
sizex=1, sizey=1,
sizing="stretch",
opacity=0.1,
layer="below"
)
)
# インタラクティブな機能を追加
fig.update_layout(
hoverlabel=dict(
bgcolor="white",
font_size=12,
font_family="Arial"
),
updatemenus=[
dict(
type="buttons",
direction="right",
buttons=[
dict(
args=[{"visible": [True] * len(fig.data)}],
label="すべて表示",
method="update"
)
],
showactive=True,
x=0.1,
y=1.1,
xanchor="left",
yanchor="top"
)
]
)
# フィギュアを表示
fig.show()
# 保存することも可能
# fig.write_html("language_evolution_tree.html")
