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")

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です