search
LoginSignup
2
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

韻を扱いたいpart8(一旦終了)

内容

前回の修正と活用法、カウント表現を行ってみる。母音の並びを単語に見立てる部分で重複があったため、重複がないように修正する。その後は前回同様テキスト中の文章をバイナリ表現で表し、コサイン類似度の高いものを表示しようと思う。カウント表現についても同様に行ってみる。

修正点

from pykakasi import kakasi
import re
import numpy as np
import pandas as pd
import itertools

with open("./test.txt","r", encoding="utf-8") as f:
    data = f.read()

#単語リスト。母音のみ使ってできる2文字から4文字の単語。775種類
word_list2 = [i[0]+i[1] for i in itertools.product("aiueo", repeat=2)]
word_list3 = [i[0]+i[1]+i[2] for i in itertools.product("aiueo", repeat=3)]
word_list4 = [i[0]+i[1]+i[2]+i[3] for i in itertools.product("aiueo", repeat=4)]
word_list = word_list2 + word_list3 + word_list4

text_data = re.split("\u3000|\n", data)
kakasi = kakasi()
kakasi.setMode('J', 'a')
kakasi.setMode('H', 'a')
kakasi.setMode('K', 'a')
conv = kakasi.getConverter()
vowel_text_list = [conv.do(d) for d in text_data]
vowel_text_list = [re.sub(r"[^aeiou]+","",text) for text in vowel_text_list]

itertoolsを使用して単語リスト部の重複がでないようにした。活用法のコサイン類似度を調べる際にも、(0,1) と(1,0)を二回計算しないために使用した。itertools

バイナリ表現

df = pd.DataFrame({"文章": text_data, "母音": vowel_text_list})
#カラム名"aa"等で、文章中に出現したら1、しなければ0
binali_dic = {}
temp = []
for word in word_list:
    for vowel in vowel_text_list:
        if word in vowel:
            temp.append(1)
        else:
            temp.append(0)
        binali_dic[word] = temp
    temp = []

for k, v in binali_dic.items():
    df[k] = v

3列目以降が"aa"等の単語に見立てた母音の並びが文章中に存在するかどうかを示している。

活用法

#コサイン類似度        
def cosine_similarity(v1, v2):
    cos_sim = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
    return cos_sim

#dfのインデックスを渡して、共通母音を返す
def common_vowel(index1, index2):
    idx = df.iloc[index1, 2:].values + df.iloc[index2, 2:].values
    vowel_word = df.columns[2:]
    common_list = [vowel_word[i] for i in range(len(idx)) if idx[i] == 2]
    return common_list

#コサイン類似度ランキング。リスト(インデックス,インデックス,cos_sim,共通母音リスト)
def cos_sim_ranking(df, threshold):
    ranking = []
    idx = itertools.combinations(df.index, 2)
    for i in idx:
        cos_sim = cosine_similarity(df.iloc[i[0]][2:].values, df.iloc[i[1]][2:].values)
        if cos_sim > threshold:
            com_list = common_vowel(i[0], i[1])
            ranking.append((i[0],i[1],cos_sim,com_list))
    return sorted(ranking, key=lambda x:-x[2])

ranking = cos_sim_ranking(df, 0.4)
for r in ranking:
    print(df["文章"][r[0]] + ":" + df["文章"][r[1]])
    print("共通母音:{}".format(r[3]))
    print()

コサイン類似度の閾値(threshold:任意の値)以上のものを、類似度が高い順に、元の文章、共通する母音の並びを出力するようにした。元の文章を倒置法等を使って、文の頭や最後に共通母音を移動させれば韻を強調できる。

カウント表現

df = pd.DataFrame({"文章": text_data, "母音": vowel_text_list})
#カラム名"aa"等で、値は出現回数
count_dic = {}
temp = []
for word in word_list:
    for vowel in vowel_text_list:
        temp.append(vowel.count(word))
    count_dic[word] = temp
    temp = []

for k, v in count_dic.items():
    df[k] = v

#dfのインデックスを渡して、共通母音、出現回数を返す
def common_vowel(index1, index2):
    idx = df.iloc[index1, 2:].values + df.iloc[index2, 2:].values
    vowel_word = df.columns[2:]
    common_list = [(vowel_word[i], idx[i]) for i in range(len(idx)) if idx[i] >= 2]
    return common_list

データフレーム作成と、common_vowelで「出現回数」を加えて返すところが違う。出力結果は同じ閾値を使っても違う結果で、出現回数まで分かるカウント表現が良いと感じた。

まとめ

テストデータでの出力は十分満足いくものとなった。カウント2以上のものを共通母音としているが、それが一つの文章でカウント2になっているものもある。これはその文章自身で韻を踏めることを示していて、思わぬ収穫となった。当初もなるべく長い分のまま扱おうとしていたのだが、「その文自身で踏める韻」を捉えられないと思い断念した記憶がある。その後、どのように分割しようか悩んで、結局文章を分割せずに扱うことになったのがおもしろい。まあ、これまでやったことがダメというわけではなく、利点や欠点が実感できたので良かった。細かい修正点や改良点はあるかもしれないが、文章を「どんな母音の並びをもっているか」で表す方法に面白みを感じたので、一度「韻を扱いたい」は終了する。

今後やりたいこと

実際のラッパーの歌詞で、今回のカウント表現を行ってみて何か発見がないかを見ていきたい。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
2
Help us understand the problem. What are the problem?