見出し画像

【心に】サンボマスター山口隆さんの詞でワードクラウドとかつくってみた【響く】

題のとおりで、とても心に響くので知りたくなってつくってみました。
ワードクラウド、共起ネットワーク、TF-IDFランキングの作成、
GPTの分析をしています。
GPTに色々教えてもらいながら作りました。GPTはもうずっとギャル語にしています。(参考:【万能語】ギャル語すごい【語学】|Yururi


確かに、君、キミ、あなたは多い気がする! 君はいたほうがいいよ♪
自分の感覚だと、サンボマスターは、僕、あなた、世界、愛、美しさ、汚さ、光、闇って感じ。闇を知ってるから明るいというか。
そしてGPTの分析・・言語化力すごい~!共感します。


ワードクラウド(単語の出現度がわかる)

共起ネットワーク(単語のつながりがわかる)

1. Kamada-Kawaiレイアウト

  • 特徴:グラフのノード間距離を、できるだけ「最短経路(グラフ上)の距離」に近づけようとして配置するアルゴリズム。

  • メリット

    • ノードがほどよく均等に広がりやすいから、ギュッと中心に固まりにくく、見やすいことが多い。

    • “長さのばらつき”が少ないので、全体が綺麗な放射状や泡状に配置されやすい。

  • デメリット

    • ノード数がめっちゃ多いと計算に時間かかるときもある。

    • 「少数精鋭のネットワーク」だといい感じだけど、巨大ネットワークになると結果が“もっさり”することも。


2. Spring (Fruchterman-Reingold) レイアウト

  • 特徴:いわゆる「ばねモデル」。ノード同士がバネでつながってるイメージで、同時に反発力も働かせてバランスをとる。

  • メリット

    • Kamada-Kawaiと同じく“力学モデル”なんで、そこそこ見やすい配置になりやすい。

    • パラメータ(k や iterations)で広がり度や収束具合を調整できる。

  • デメリット

    • ノード数が増えると計算量が多いし、うまくパラメータ調整しないと中心でごちゃっと固まることも。

    • 同じ設定でもたまに結果がちょいランダム要素入る。


3. Circularレイアウト

  • 特徴:ノードを円周に一列に並べる。

  • メリット

    • めっちゃシンプルに「ポーンと円形に配置」するだけだから、見た目はスッキリ。

    • ノードの数が多くても、とりあえず円形にズラッと並ぶだけで判読しやすい。

  • デメリット

    • 実際のノード間の関係性はあんま反映されない。

    • 「誰と誰が強く繋がってるのか」見るにはエッジが内側の方でごちゃりがち。


4. Shellレイアウト

  • 特徴:**殻(shell)**を何層か重ねるようにノードを並べるレイアウト。

    • “亀の甲羅みたい”って言われるのはこれかも! たとえば「重要ノード集を最内殻、周辺ノードを外殻」って感じで同心円状に並べる。

  • メリット

    • グループ分け(コミュニティやレベルなど)があるときに、そのグループごとに層を分けると綺麗にまとまる。

    • 某同心円状ネットワーク図みたいにオシャレ。

  • デメリット

    • どのノードをどのshellに入れるか指定する必要がある場合もある。自動的にはやってくれないことも。

    • 大量のノードだと外殻が広すぎたり、内殻で密集したりするかも。


5. Randomレイアウト

  • 特徴:名前の通りランダムに配置するだけ。

  • メリット

    • 計算が超速い。

    • 試しに「強調なしで1回ラフに見たい」ときに使える。

  • デメリット

    • ただのランダムなので、ほぼ読み取りにくいw

    • “わざわざこれ使うメリット”はあんま無いかも。


6. Spectralレイアウト


なんかスカスカになるのあるあるだそう
  • 特徴:グラフのラプラシアン行列の固有ベクトルを使う数学的な配置。

  • メリット

    • 力学モデルじゃないアプローチで、固有ベクトルに従って並べる。大きいグラフでもそこそこ速い。

    • 直線的な並びになることが多く、階層や構造を把握しやすいときがある。

  • デメリット

    • 数学っぽくてパラメータいじりにくい。

    • ややノードが線上にずらーっと並びがちになって「なんか狭い」感じになるかも。


7. まとめ & コツ

  • Kamada-Kawai:ノードが綺麗に均等に広がりやすく、スッキリ見える → イチオシ!

  • Spring(Fruchterman-Reingold):これも定番。kとかでチューニングしやすいから人気。

  • Shell:グループを同心円で分けたいとき、亀甲っぽくてオシャレ。

  • Circular:単純に円周に並べるから、見やすさは一定だけど構造はわかりにくい。

  • Random:とりあえずランダム。ほぼデモ用かな?

  • Spectral:固有値計算による配置。大規模グラフでも安定して動くけど、ちょい硬派。

最終的には**「自分が何を見たいか」**でレイアウト選ぶといいよ~。たとえば

  • コミュニティ構造を際立たせたい→ShellやSpring

  • とにかく見やすく均一に→Kamada-Kawai

  • さくっと円周に並べて拡散の具合を見たい→Circular

って感じで、色々試して一番見やすいものをチョイスするのがベストだと思う♪

亀の甲羅っぽいShellとか、均等に広がるKamada-Kawaiとか、好みで使い分けて、最強に見やすいネットワーク図を作っちゃってね~☆


TF-IDFランキング(その文ならではのキーワードがわかる)

曲全体・・複数曲間共通の頻出単語を打ち消し、特徴的な単語がわかる

→「どの曲でも当たり前に書かれてる単語」が薄まり、「一部の曲でだけ、強烈に書かれている単語」が見えてくる

Rank Word TF-IDF Score
1 あなた 0.0546
2 キミ 0.0499
3 世界 0.0335
4 生きる 0.0281
5 日々 0.0277
6 見る 0.0258
7 僕ら 0.0258
8 もの 0.0255
9 全て 0.0243
10 いく 0.0236
11 言う 0.0233
12 消える 0.023
13 美しい 0.0229
14 笑う 0.0227
15 忘れる 0.022
16 明日 0.0219
17 愛しい 0.0216
18 知る 0.0214
19 今夜 0.0206
20 強い 0.02

特定の曲・・複数曲間共通の頻出単語を打ち消し、その曲ならではの単語がわかる

例)歌声よおこれ
Rank Word TF-IDF Score
1 歌声 0.655
2 響く 0.299
3 越える 0.232
4 これ 0.1921
5 歌う 0.1522
6 逢う 0.1495
7 愛しい 0.1427
8 あなた 0.1407
9 知る 0.1361
10 感動 0.1014

TF-IDFスコアの見方

1. 結論:絶対的な基準値はなく、相対的に見る

  • TF-IDFは「それぞれのコーパス(文書集合)によってスコアの上限・下限が変わる」もの。

  • だから「0.05以上なら強い!」みたいな絶対的なボーダーはないんだよ~。

  • 同じデータセット内で「他の単語と比べて高いか低いか」を見るのが大事。


2. よくある「TF-IDFスコアの範囲」

  1. 最大値:1.0 付近

    • ひとつの文書の中で、他の文書に登場しない単語が繰り返し多用されると、(理論上)1.0に近くなる可能性がある。

    • でも実際にはまれ。

  2. 0.0 に近い

    • どの文書にも広く分散して出る超ありふれた単語。

    • あるいは、ほとんど出てこない単語。

  3. 0.1~0.3 あたりが比較的「そこそこ特徴的」と感じられる範囲になるケースが多い印象。

    • ただし、これはあくまで経験則。文書数や単語の数が変わると、だいぶ上下する。


3. どう判断する?

3-1. トップ何単語かを比較

  • たとえば「TF-IDFスコア上位10単語」のなかで0.2が最高なら、「0.2がこの曲では最強ワードなんだ」って分かる。

  • 逆に「スコアの幅が 0.03 ~ 0.20 くらい」なら、0.20は抜けて高いし 0.03は弱い、とか相対比較するといい。

3-2. 全体平均や中央値との比較

  • 全行(曲)の平均TF-IDFをとって「それ以上か以下か」で、強め or 弱めをふんわり判断できる。

  • スコア分布をヒストグラムなどにして、「上位何%くらいに入るスコアか」を見る方法もある。

3-3. 閾値を決める

  • 分析目的次第で「TF-IDFスコアが 0.05 以上の単語だけ抽出しよう」みたいな独自ルールを設定するときもある。

  • これは本当にプロジェクトや分析者の判断。より“特徴が強い”単語を取りたいときに使うね。


4. まとめ

  1. 絶対的基準はない

    • TF-IDFは相対評価。同じコーパス内で高いか低いかを比べるのがベスト。

  2. トップN単語で比較

    • 「スコアが高い順に並べて、上位10~20を見る」のが一番わかりやすい見方。

  3. 閾値や分布で調整

    • 自分の分析目的に合わせて「0.05以上だけ注目」「上位5%だけ注目」などの方法をとると、特徴がある単語を上手く抜き出せる。

なので、「何点以上が強いの?」って厳密な答えはないけど、上位ランキングの数値が他と比べて突出してたら“この単語はかなり特徴的!”って判断してOKだよ。



使ったコード

ワードクラウド

from wordcloud import WordCloud
import matplotlib.pyplot as plt
from janome.tokenizer import Tokenizer
from collections import Counter

# ---- 不要な単語をリストにしておく ----
exclude_words = [
    "する", "ある", "いる",   # もともと例示した不要語
    "てる", "ん", "(",")","くる" ,"さ" ,"こと" ,"何" ,"せる","なる" ,"の","歌詞" ,"!!"         # 今回追加したい不要語
]

# ---- 1. テキストファイルから読み込む ----
with open('kashi.txt', 'r', encoding='utf-8') as f:
    text = f.read()

# ---- 2. 形態素解析で単語を抽出 ----
t = Tokenizer()
words = []
allow_pos = ["名詞", "動詞", "形容詞", "形容動詞"]

for token in t.tokenize(text):
    part_of_speech = token.part_of_speech.split(',')[0]
    base_form = token.base_form  # 動詞などの原形を取得

    # 品詞チェック + 不要単語でないかチェック
    if part_of_speech in allow_pos and base_form not in exclude_words:
        words.append(base_form)

# ---- 3. 出現回数カウント (オマケ) ----
counter = Counter(words)
print("【単語の頻出数トップ10】")
for w, c in counter.most_common(10):
    print(w, c)

# ---- 4. ワードクラウドのためスペース区切り文字列にする ----
word_chain = ' '.join(words)

# ---- 5. WordCloudを生成 ----
font_path = r"C:\Windows\Fonts\meiryo.ttc"
wc = WordCloud(
    background_color="white",
    font_path=font_path,
    width=800,
    height=600
).generate(word_chain)

# ---- 6. プロットして表示 ----
plt.figure(figsize=(10, 7))
plt.imshow(wc, interpolation="bilinear")
plt.axis("off")
plt.show()


共起ネットワーク


import networkx as nx
from itertools import combinations
from janome.tokenizer import Tokenizer
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as colors

# ---- 不要単語リスト ----
exclude_words = [
    "する", "ある", "いる",
    "てる", "ん","く","手","二","いい","ため",
    "(",")","くる","さ","こと","何","せる","なる","の","歌詞","!!",
    "れる", "どこ","合う","られる","事","しまう","くれる","等","ない","よう","中","それ",
    "ちまう","おくれる"
]

# ---- 品詞の対象 ----
allow_pos = ["名詞", "動詞", "形容詞", "形容動詞"]

# ---- 1. テキストファイルを行単位で読み込む ----
with open('kashi.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()

# ---- 2. 形態素解析してトークン化 ----
t = Tokenizer()
all_line_tokens = []

for line in lines:
    tokens_in_line = []
    for token in t.tokenize(line):
        part_of_speech = token.part_of_speech.split(',')[0]
        base_form = token.base_form  # 動詞は原形にする
        if part_of_speech in allow_pos and base_form not in exclude_words:
            tokens_in_line.append(base_form)
    all_line_tokens.append(tokens_in_line)

# ---- 3. 同じ行で共起した単語ペアをカウント ----
co_occurrence_dict = {}
for tokens in all_line_tokens:
    unique_tokens = set(tokens)
    for w1, w2 in combinations(sorted(unique_tokens), 2):
        co_occurrence_dict[(w1, w2)] = co_occurrence_dict.get((w1, w2), 0) + 1

# ---- 4. NetworkXグラフ生成 ----
G = nx.Graph()

# ---- 共起回数が一定数以上のペアだけエッジを追加 ----
threshold = 30  # 好きに変えてOK。大きいほど厳選される。
for (w1, w2), weight in co_occurrence_dict.items():
    if weight >= threshold:
        G.add_edge(w1, w2, weight=weight)

# ---- 5. エッジの色・太さ用意 ----
if len(G.edges()) > 0:
    weights = [G[u][v]['weight'] for u, v in G.edges()]
    min_w, max_w = min(weights), max(weights)
    norm = colors.Normalize(vmin=min_w, vmax=max_w, clip=True)
    cmap = cm.ScalarMappable(norm=norm, cmap=cm.Reds)
    edge_colors = [cmap.to_rgba(w) for w in weights]
    edge_widths = [1 + (w - min_w) for w in weights]
else:
    # エッジが無い場合、適当にデフォルト値をセット
    edge_colors = []
    edge_widths = []

# ---- 6. 複数レイアウトをまとめて可視化 ----
layouts = {
    "Kamada-Kawai Layout": nx.kamada_kawai_layout,
    "Spring Layout (Fruchterman-Reingold)": nx.spring_layout,
    "Shell Layout": nx.shell_layout,
    "Circular Layout": nx.circular_layout,
    "Random Layout": nx.random_layout,
    "Spectral Layout": nx.spectral_layout
}

for layout_name, layout_func in layouts.items():
    # レイアウト計算(春っぽいやつだけパラメータ指定)
    if "Spring" in layout_name:
        pos = layout_func(G, k=5.0, iterations=300)  # お好みでkやiterationsを増やす
    else:
        pos = layout_func(G)

    # ---- figureを新しく作ってレイアウトごとに表示 ----
    plt.figure(figsize=(12, 8))

    # (A) エッジ描画
    nx.draw_networkx_edges(
        G, pos,
        edge_color=edge_colors,
        width=edge_widths,
        alpha=0.8
    )

    # (B) ノード描画
    nx.draw_networkx_nodes(
        G, pos,
        node_size=500,
        node_color="lightblue"
    )

    # (C) ラベル描画
    nx.draw_networkx_labels(
        G, pos,
        font_size=10,
        font_family="Meiryo"
    )

    plt.title(f"{layout_name} (threshold={threshold})", fontname="Meiryo", fontsize=14)
    plt.axis("off")
    plt.tight_layout()
    plt.show()

TF-IDFランキング

import csv
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from janome.tokenizer import Tokenizer

# ---- 不要単語リスト ----
exclude_words = [
    "する", "ある", "いる",
    "てる", "ん","く","手","二","いい","ため",
    "(",")","くる","さ","こと","何","せる","なる","の","歌詞","!!",
    "れる", "どこ","合う","られる","事","しまう","くれる","等","ない","よう","中","それ",
    "ちまう","おくれる","いく"
]

# ---- 品詞の対象 ----
allow_pos = ["名詞", "動詞", "形容詞", "形容動詞"]

# ---- 1. kashi.txt を行単位で読み込み ----
with open('kashi.txt', 'r', encoding='utf-8') as f:
    lines = f.readlines()

# ---- 2. 形態素解析して行ごとのスペース区切り文字列へ ----
tokenizer = Tokenizer()
documents = []  # 1行(曲)ごとに格納するリスト

for line in lines:
    tokens_in_line = []
    for token in tokenizer.tokenize(line.strip()):
        part_of_speech = token.part_of_speech.split(',')[0]
        base_form = token.base_form
        if part_of_speech in allow_pos and base_form not in exclude_words:
            tokens_in_line.append(base_form)
    # スペース区切り文字列を"1文書"として追加
    documents.append(" ".join(tokens_in_line))

# ---- 3. TF-IDF計算 ----
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(documents)  # shape: (行数, 単語数)
feature_names = vectorizer.get_feature_names_out()

# ---- 4. 全体平均TF-IDFランキング(上位20)をCSVに出力 ----
avg_tfidf = tfidf_matrix.mean(axis=0).A1   # 全文書(行)の平均
sorted_indices = np.argsort(avg_tfidf)[::-1]  # スコアが高い順に並べ替え

with open('tfidf_avg_ranking.csv', 'w', encoding='utf-8-sig', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(["Rank", "Word", "TF-IDF Score"])
    for i in range(20):
        idx = sorted_indices[i]
        writer.writerow([i+1, feature_names[idx], f"{avg_tfidf[idx]:.4f}"])

print("tfidf_avg_ranking.csv に全体平均の上位20単語を書き出しました。")

# ---- 5. 各行(曲)のトップ10を tfidf_by_doc.csv に出力 ----
with open('tfidf_by_doc.csv', 'w', encoding='utf-8-sig', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(["DocIndex", "Rank", "Word", "TF-IDF Score"])
    
    for doc_idx in range(len(documents)):
        # doc_idx行目(曲)のベクトル
        row_vector = tfidf_matrix[doc_idx, :].toarray().flatten()
        sorted_indices = np.argsort(row_vector)[::-1]
        
        top_n = 10
        for rank in range(top_n):
            idx = sorted_indices[rank]
            writer.writerow([
                doc_idx+1,  # +1すると人間用の「n行目」っぽい
                rank+1,
                feature_names[idx],
                f"{row_vector[idx]:.4f}"
            ])

print("tfidf_by_doc.csv に各行(曲)のTF-IDFトップ10を書き出しました。")


GPT o1proの分析:オッケーまじエモすぎる山口隆センセの歌詞、読んでてガチ胸アツじゃん?


ここにまとめられてるやつ、ザックリ全部見た感じ、めっちゃ「愛」とか「命」とか「自由」とか「まじで大事なこと!」ってキーワードがゴロゴロしてて、読んでると心臓ドキドキ止まんないレベルなんですけど~!

1. 愛と命と涙の3大エモキーワード

  • :いやもう「I Love You」連呼してたり、「君を守りたいんだ」とか超ガチ勢。恋愛のトキメキだけじゃなくて、友情とか人間同士のキズナもフツーに愛でまとめてる感じだし、そこに「信じたい」「守りたい」「一緒に泣きたい」みたいなポジメッセがふんだんに盛り込まれてるワケよ。

  • :わりと「命は儚いけど超尊い」みたいな視点があるっしょ?「生命かけて踊ってやんよ!」みたいな激アツな姿勢もあれば、「生きたくてたまらないし、怖いけどワンチャンやりきりたい」みたいな歌詞も散見されて、もはや生への執着ガチパネェっす。

  • :山口隆センセの詩って、悲しみドーンからの「でも一緒なら泣いてもOK!むしろ乗り越えようぜ!」ってスタイルが満載。泣くほど切ない感情をガツガツ表現してるけど、その分ラブや希望もズッシリくる!まじエモ~。

2. 「夜・闇」と「朝・光」のコントラスト

サンボマスターといえば、「夜がやべぇほど暗いけど、ちゃんと光来るから大丈夫やで」的な流れがザ定番じゃん?

  • 夜闇パート:悲しみや孤独を抱えた描写が度々出てくるし、「誰も信じらんなくて泣いちゃう」ってへこむシーンが何曲にも登場。

  • 朝光パート:「夜明けとともに、さびしみを消し飛ばす」「朝日が差し込んで君と笑える」みたいな歌詞で、「明けない夜はない!」って力強さを毎回ガンガン提示してくるのよ。暗闇を飲み込んだ後の「希望」ってとこが、まじ人間賛歌すぎてヤバみ~。

3. 自由&革命マインド

「こういうズルイ世界だけど、オレらが変えてやんよ!」みたいな胸アツ表現がめちゃ登場してるッスね。

  • 革命のキーワード:単に恋愛とかじゃなくて、社会とか世界を変えたいくらいのノリがあるっしょ。「世界を変えさせておくれよ」「ロックはくたばんねー」「奇跡を起こしたいんだ」みたいな。わりとセンセーショナルなフレーズ多め。

  • 自由:一緒に踊ってぶっ壊そーぜ!みたいなさ。「縛られたくない!」「鎖は外す!」の精神がビシビシ伝わる。ちょいパンクなノリもあるし、でもそれ全部“愛”と“生”に直結してるのがまじアツ。

4. ふたりきりでも強くなれる

「ふたり(あるいはみんな)なら困難ブッ飛ばせる!」みたいな協力プレイ精神が通底してて尊い。単に「君とぼくがラブラブ」ってだけじゃなくて、「あんたとオレが組めばこっちのもんでしょ?」的な連帯の強さもあって、「弱くても一緒なら生きてやる!」みたいな逆転感スゴい。

5. 全体のまとめ

めちゃ長いリストだけど、共通してるのは

  1. 強烈な愛の叫び

  2. 涙を否定しないで、むしろ武器にしてく感じ

  3. 世界や社会を変えちゃうほどの熱量

  4. 夜と朝のギャップで人間の“苦しみと希望”を表す

って流れがすっごい目立つのね。だから読んでると落ち込んでても「いや、やれるかも~?」って思えるし、悲しさもめっちゃ尊いんだって教えてくれる。シンプルにサンボマスターらしい熱いパッションまる出しで、ロック魂と愛とか尊厳のメッセージが爆発してるって感じ~。

つまりまとめると、「愛と自由と未来への希望」を全身で歌い倒す、めっちゃエモ&熱血スタイルってこと!どの曲も「君もオレも命が響きあうから、大丈夫やって信じていこう!」みたいなバイブス満載で、ヤバいくらい胸に刺さるっしょ。悲しみも肯定するってのが最高の魅力だね。

いや~、この歌詞群読むだけで青春再燃しちゃうレベルだから、もうガンガン聞いて燃えるしかなくね~!?マジでエモよ、マジ最高~!!

ざっくりキーワードをガンガンまとめるとこんなカンジ

  • 愛(I Love You、守る、とか恋愛系)

  • 命(生きる、死にたくない、生命の輝きとか)

  • 涙・悲しみ(泣いたり傷ついたり、それでも乗り越える)

  • 夜と朝(闇と光、夜明けで希望ゲット!みたいな)

  • 自由(縛られたくない、鎖ぶっ壊そーぜ的な)

  • 希望・奇跡(「絶望ブッ飛ばす!」な革命ノリ)

  • ふたり(キミとボク、絆、連帯感)

  • 世界を変える・革命(社会ガチ変えたい感、やらかしたい情熱)

  • 音楽・踊り(踊って騒いで愛し合おうみたいなパンク魂)

こんな感じのワードたち、めっちゃ目立ってたよ!


いいなと思ったら応援しよう!