BETA

【動画解説】にじさんじライバーの関係をツイートで分析してみる

投稿日:2019-10-23
最終更新:2019-10-23
※この記事は外部サイト(https://qiita.com/ganariya/items/3c6616c87...)からのクロス投稿です

動画リンク

https://www.youtube.com/watch?v=XzFuPVqlJVU

はじめに

にじさんじ、面白いですよね!
本当に多種多様な人材が居ます。

当然そうなると似た性格の人や、似た発言の人がいるかと思います。
そこで、pythonを用いてライバーのTwitterを取得し、if-idf+cosine_similarityを求めて、どのライバーがどう似ているのか?を調べようと思います。

Twitterからリストを持ってくる

まず、にじさんじライバーのTwitterの情報を取得するために、Twitterアカウントリストがほしいです。

そこで
にじさんじのメンバーリスト
からTwitterのアカウント・URLを取得してみます。

まず

  • requests
  • BeautifulSoup

を利用してスクレイピングを行います。
この2つに関しては非常に記事が多いため、そちらを参考にしてみてください!

まず、さきほどのメンバーリストを直接スクレイピングしてみました。

しかし、取得したページに含まれているアカウント数は10も満たず、どうやらこの方法ではTwitterのリアルタイム取得に対応できないため、きちんと全員分集められないようです。

そこで、twitter-pythonというライブラリを用いてメンバーの情報を取得します。

これでjsonに

[{"id": 1184071904245583874, "name": "星川サラにじさんじ", "screen_name": "Sara_Hoshikawa"}, {"id": 1173907942799601664, "name": "早瀬 走♀♀", "screen_name": "SouHayase"}, {"id": 1165929239436120064, "name": "山神カルタ ", "screen_name": "Karuta_Yamagami"}, {"id": 1163457518439284738, "name": "シェリン・バーガンディ", "screen_name": "ShellinBurgundy"}, {"id": 1162577611228233729, "name": "フミ にじさんじ所属Vライバー 次回配信22日(火)24:00", "screen_name": "FumiVirtual"}, {"id": 1158352011990990850, "name": "エリー・コニファー10/28-29ポ三家お泊まり初オフコラボ配信《にじさんじ所属》", "screen_name": "Eli_Conifer"}, {"id": 1146679484575170560, "name": "健屋 花那(すこや かな)", "screen_name": "sukosuko_sukoya"}, {"id": 1146300489832837125, "name": "天宮こころまいにち夕方配信中!《にじさんじ所属》", "screen_name": "amamiya_kokoro"}, {"id": 1144558351788859393, "name": "ラトナ・プティ《にじさんじ所属》", "screen_name": "ratna_petit"}, {"id": 1144541744639250432, "name": "夜見れなにじさんじ所属", "screen_name": "rena_yorumi"}, {"id": 1144540365854167041, "name": "葉加瀬 冬雪 / Hakase Fuyuki", "screen_name": "Hakase_Fuyuki"}, {"id": 1144142348873392129, "name": "黛 灰@にじさんじ", "screen_name": "mayuzumi_X"}, {"id": 1144129307637239809, "name": "アルス・アルマルにじさんじ", "screen_name": "ars_almal"}, {"id": 1143824438040748032, "name": "相羽 ういは @22日22時~歌ってみた投稿!", "screen_name": "AibaUiha"}, {"id": 1141971886688989184, "name": "加賀美 ハヤト", "screen_name": "H_KAGAMI2434"}, {"id": 1140590504158957568, "name": "ニュイ・ソシエール", "screen_name": "Nui_Sociere"}, {"id": 1140588169491935233, "name": "葉山舞鈴", "screen_name": "Hayama_Marin"}, {"id": 1126062450107768832, "name": "レヴィ・エリファは歌動画をあげたイ", "screen_name": "Levi_E_2434"}, {"id": 1125700985660198912, "name": "エクス・アルビオ【にじさんじ所属】エビオ", "screen_name": "Ex_Albio"}, {"id": 1120529071585107968, "name": "雪城眞尋@にじさんじ所属", "screen_name": "MahiroYukishiro"}, {"id": 1110711077468168192, "name": "三枝 明那さえぐさ あきな", "screen_name": "333akina"}, {"id": 1107968804447903744, "name": "鈴原るるボイス販売中", "screen_name": "lulu_suzuhara"}, {"id": 1107935587271467008, "name": "リゼ・ヘルエスタ", "screen_name": "Lize_Helesta"}, {"id": 1107868757156745216, "name": "愛園 愛美", "screen_name": "manami_aizono"}, {"id": 1107557844855844864, "name": "戌亥 とこ", "screen_name": "inui_toko"}, {"id": 1099996270947528704, "name": "アンジュ・カトリーナ", "screen_name": "Ange_Katrina_"}, {"id": 1088046328423141380, "name": "語部紡", "screen_name": "KataribeTsumugu"}, {"id": 1088029978761977856, "name": "瀬戸美夜子", "screen_name": "seto_miyako"}, {"id": 1088023486583304192, "name": "御伽原 江良 ", "screen_name": "OtogibaraEra"}, {"id": 1086415881154875392, "name": "小野町春香22日20:00コラボ 23:00歌【にじさんじ所属】", "screen_name": "onomachi_haruka"}, {"id": 1085498064645705728, "name": "夢月ロア22日ポケモンX", "screen_name": "yuzuki_roa"}, {"id": 1085375212575571968, "name": "郡道美玲おさんぽボイス発売中", "screen_name": "g9v9g_mirei"}, {"id": 1082065005061652480, "name": "童田明治ねむい", "screen_name": "warabeda_meiji"}, {"id": 1043543347430797312, "name": "矢車りね @ ボイス販売中", "screen_name": "Rine_Yaguruma"}, {"id": 1043029918014009345, "name": "黒井しば", "screen_name": "BlackShiba_chan"}, {"id": 1034345063432650752, "name": "ジョー・力一(りきいち)", "screen_name": "JoeRikiichi"}, {"id": 1034137905000636417, "name": "でびでび・でびる月曜22時定期配信", "screen_name": "debidebiru_sama"}, {"id": 1028458841871007744, "name": "町田ちま", "screen_name": "chima_machita23"}, {"id": 1027050557465223169, "name": "舞元啓介舞スバ公式グッズ発売決定!", "screen_name": "maimoto_k"}, {"id": 1026998487701893120, "name": "月見しずく@ .  . ", "screen_name": "tukimi_sizuku"}, {"id": 1023138752850321408, "name": "雪汝卍", "screen_name": "setsuna2434"}, {"id": 1022844567735824384, "name": "椎名唯華", "screen_name": "yuika_siina"}, {"id": 1022782812775100416, "name": "魔界ノりりむ地頭が良い", "screen_name": "makaino_ririmu"}, {"id": 1019625123114987521, "name": "雨森 小夜", "screen_name": "Sayo_Amemori"}, {"id": 1019541420728713216, "name": "遠北 千南", "screen_name": "ac1kt"}, {"id": 1019066007447470080, "name": "夢追翔a.k.a. ゆめお", "screen_name": "kakeru_yumeoi"}, {"id": 1015204476242677761, "name": "神田笑一", "screen_name": "Kanda_Shoichi"}, {"id": 1012211447160455170, "name": "笹木咲", "screen_name": "saku_sasaki"}, {"id": 1011952508317548550, "name": "飛鳥ひな", "screen_name": "hina__asuka"}, {"id": 1011167857596493824, "name": "本間ひまわりさんじはんにつぼ", "screen_name": "honmahimawari"}, {"id": 1009751809966010368, "name": "竜胆 尊 / Rindou Mikoto", "screen_name": "RindouMikoto"}, {"id": 1009734224369221632, "name": "鷹宮リオン", "screen_name": "TakamiyaRion"}, {"id": 1007214007827091456, "name": "ベルモンド・バンデラス", "screen_name": "belmond_b_2434"}, {"id": 1004740613814681601, "name": "春崎エアル@にじさんじ", "screen_name": "harusakiair2434"}, {"id": 1003299893404815360, "name": "成瀬鳴", "screen_name": "narusenaru_2434"}, {"id": 1002342365695078401, "name": "社築", "screen_name": "846kizuQ"}, {"id": 1002077473360658432, "name": "卯月コウ", "screen_name": "udukikohh"}, {"id": 1002075894880452609, "name": "安土桃", "screen_name": "momo_aduchi"}, {"id": 1002073779848151041, "name": "ドーラ", "screen_name": "___Dola"}, {"id": 1002059898929082370, "name": "出雲霞にじさんじの学習型AI", "screen_name": "ikasumi_zzz"}, {"id": 1002050003559317504, "name": "轟京子", "screen_name": "KT_seeds"}, {"id": 1001291760272736257, "name": "緑仙", "screen_name": "midori_2434"}, {"id": 1000324666697728000, "name": "シスター・クレア", "screen_name": "SisterCleaire"}, {"id": 999942020238995456, "name": "花畑チャイカwe are にじさんじ!!", "screen_name": "ZulmIhP1nlMOT5y"}, {"id": 994984072647593984, "name": "鈴木勝にじさんじ", "screen_name": "Darkness_Eater"}, {"id": 988489581367513088, "name": "赤羽葉子", "screen_name": "Youko_Akabane"}, {"id": 988101299106267138, "name": "叶@歌ってみた「野狗子」投稿", "screen_name": "Kanae_2434"}, {"id": 973784758688927745, "name": "森中花咲22(火)20時UNOコラボ", "screen_name": "KazakiMorinaka"}, {"id": 971925378913611776, "name": "文野環にじさんじの野良猫", "screen_name": "nekokan_chu"}, {"id": 971316705363464192, "name": "宇志海いちご@にじさんじ所属", "screen_name": "ushimi_ichigo"}, {"id": 971032696393789440, "name": "ギルザレンⅢ世【にじさんじ所属】", "screen_name": "Gilzaren_III"}, {"id": 970940146680868864, "name": "鈴鹿詩子 不定期配信&ネタ動画投稿", "screen_name": "suzukautako"}, {"id": 970692564096499712, "name": "剣持刀也にじさんじ", "screen_name": "rei_Toya_rei"}, {"id": 970660632834920449, "name": "物述有栖JK証明週間", "screen_name": "AliceMononobe"}, {"id": 970645643965317126, "name": "夕陽リリ", "screen_name": "Yuuhi_Riri"}, {"id": 970645330956963840, "name": "伏見ガク(20)†にじさんじ所属", "screen_name": "gaku_fushimi"}, {"id": 970618643120664576, "name": "家長 むぎにじさんじ", "screen_name": "ienaga_mugi23"}, {"id": 965760241169088512, "name": "葛葉", "screen_name": "Vamp_Kuzu"}, {"id": 964340295914569728, "name": "桜 凛月  SAKURA RITSUKI", "screen_name": "SAKURA_RITSUKI"}, {"id": 958767484902957056, "name": "勇気 ちひろ", "screen_name": "Chihiro_yuki23"}, {"id": 958737597336928256, "name": "月ノ美兎22日20時~配信", "screen_name": "MitoTsukino"}, {"id": 958726740108484608, "name": "える[email protected]にじさんじ所属", "screen_name": "Elu_World"}, {"id": 958695135008628737, "name": "渋谷ハジメ(オワリ)@にじさんじ", "screen_name": "sibuya_hajime"}, {"id": 958675678689243137, "name": "鈴谷アキにじさんじ所属", "screen_name": "aki_suzuya"}, {"id": 958646957190217728, "name": "樋口楓にじさんじ所属", "screen_name": "HiguchiKaede"}, {"id": 958632495196459009, "name": "モイラ@にじさんじ所属の女神", "screen_name": "Moiramoimoimoi"}, {"id": 958629229330968580, "name": "静凛FF8のしずりん", "screen_name": "ShizuRin23"}]  

ライバーのユーザー情報を集めることができました。

ライバーのツイートを集める

今度は、本題のライバーのツイートを収集します。
収集したツイートはtweetsというフォルダに、各ライバー名ごとに保存することにします。
先程得たライバーのユーザーIDから、ライバーのツイートを取得していきます。

import json  
import twitter  
import os  
import time  

account_id_list_file_name = "account_id_list.json"  
tweets_dir = 'tweets'  

api = twitter.Api(consumer_key="",  
                  consumer_secret="",  
                  access_token_key="-",  
                  access_token_secret="")  

with open(account_id_list_file_name) as f:  
    members_info = json.load(f)  

for member in members_info:  
    id = member['id']  
    name = member['name'].replace('/', '').replace('\\', '')  
    screen_name = member['screen_name']  

    tweets = api.GetUserTimeline(user_id=id, count=200)  
    tweets_list = []  
    for tweet in tweets:  
        txt = tweet.text  
        tweets_list.append(txt.strip())  

    all_tweet = ''.join(tweets_list)  

    if not os.path.isdir(tweets_dir):  
        os.mkdir(tweets_dir)  

    tweet_path = os.path.join(tweets_dir, name + '.txt')  

    with open(tweet_path, mode='w') as f:  
        f.write(all_tweet)  

    time.sleep(2)  

例えば卯月コウのファイルを見てみます。

もうすぐ!  

# 3 -俺がSEKIROで上手に忍びすぎて伊賀伊賀でワロタwwwwww【にじさんじ/卯月コウ賀】 https://t.co/BjlyoZLZF9 @YouTubeさんから唐突だけど予定なくなったのでこの後17時半からセキロしますセキロめっちゃ面白い  

明日はたぶん配信なしですはじめ  

# 1 -俺がSEKIROクリアできなくておまえらイライラでワロタwwwwww【にじさんじ/卯月コウ】 https://t.co/BjlyoZLZF9 @YouTubeさんからぽはよう。  
昼か夕方か怪しいが16時からやります!!  

# 2 -俺がSEKIROで死ぬたびにおまえら喉イガイガでワロタwwwwww【にじさんじ/卯月コウ】 https://t.co/BjlyoZLZF9 @YouTubeさんからはじめ  

#1 俺がSEKIROクリアできなくておまえらイライラでワロタwwwwww【にじさんじ/卯月コウ】 https://t.co/cW5Qxv0svC @YouTubeよりご飯食べすぎてお腹痛い( ;  ; )  
30分ほど遅刻しますもうちょいやってもよかったけど、クソ配信としては短めにして今後配信中擦ったほうがネタもワインも美味しいという思惑の即切り10分から始めます  

5秒以上黙ったら即終了!メンタリストKOHHによる超心理学雑談 https://t.co/BjlyoZLZF9 @YouTubeさんから19時から雑談系の配信  
23時からポケモンBWかセキロやる。  
できたら明日の昼もなんかやりたい。  
以上!動画あげました  
見てね  

リヴァイだぁ(卯月コウ) https://t.co/tph06YvZsJ @YouTubeより見てくれた人ありがとう!  
明日は18時に短めの動画あげからみてーーー!!もうすぐ  

Re:ゼロから始めるポケモンBW生活 【ゲーム実況# 3 】バッジ6個目標 https://t.co/SOUyE8zIlP @YouTubeより何か  
する  
20時半から  

内容は迷い中表紙絵になったぞ~!!かわいい!  

めっちゃ頑張ってNGをかいくぐって【ハロウィンに渋谷のトラック横転させるボイス】を実現したのでお楽しみに https://t.co/EwM97R09M7メンバー特典にプリンアラモード(@Purin_a_La_Mode)さんが書いてくれた処刑くんスタンプを追加しました~!  

プリンアラモードさんの単行本  

取れてそうです! 

tf-idfを求めて各ライバーの特徴量を求める

各ライバーがどのような特徴的な発言をしているかを、サンプルケースがかなり少ない気がしますが(83人しかいない)、tf-idf値で求めていこうと思います。

tf-idfとは

tf-idfは、各文書の重要なキーワードを取り出す自然言語処理のアルゴリズム・計算方式です。

例えば、ライバーのツイートなら

  • ライバーの名前
  • ライバーの趣味

などが重要なキーワードになる可能性があります。

また、

  • は・が
  • です・ます

などは殆どの文書に現れるため、重要なキーワードとしてカウントされてほしく有りません。

このように、文書の重要な特徴づけるキーワードを求めるものがtf-idf値になります。

tf-idfは、tf値とidf値をかけ合わせたものになります。簡単ですね。

tf

tfはTerm Frequencyで、単語の出現頻度を表します。
つまり文書に多く含まれる単語ほどそりゃまあ重要だよなぁ!という指標です。

政治系のニュースなら安倍の単語はよく出ます。
よって、安倍が重要な単語とtfによってカウントされるはずです。

計算式は

$$tf(t, d) = {\frac{単語tの出現頻度}{文書dのすべての単語数}}$$

のように計算します。

上記の計算式は文書$d$における単語$t$の$tf$値を計算しています。
よって、単語$t$に関するベクトルみたいな感じになります。

tfの例

例えば文書$d = $競技プログラミングが大好きです。競技プログラミングでお金を稼ぐ。とします。

まず、この文書$d$を単語に分けると、(競技プログラミング が 大好き です 競技プログラミング で お金 を 稼ぐ)
となります。
よって、この文書の単語数は$9$です。

そして、$tf$値は

$tf(競技プログラミング, d) = 2/9$, $tf(お金, d) = 1/9$のようになります。

ゆえに、この文書に関するtf値は、競技プログラミングに関してのみ$2/9$、それ以外の単語は$1/9$の$8$次元のtf値のベクトルとして見ることができます。
そして、最もtf値が高い競技プログラミングがこの文書のキーワードと言えそうです。

tfの欠点

ただ、このtfのみでは文書のキーワードを求めることができません。

例えば、$d = $私の彼の夫の弟の姉は姉だみたいな文章があったとします。(ない.。。)
このとき、この文書で一番大きいtf値はなんでしょうか?

当然、「の」になってしまいます。一番出現頻度が大きいからです。
このように出現頻度が大きいだけでは、このキーワードが重要かどうかは計ることができません。
そこで、$idf$の概念を取り入れます。

idf

$idf$はInversed Document Frequencyの略であり、日本語では逆文書頻度といいます。

この値で分かることは、ある単語$t$がより多くの文書に含まれているとしたとき、その単語$t$はあまり特徴的とは言えない、ということです。
例えば、ですのような単語は、日本語の文書を$2000$個集めたら、$1990$個くらいには入ってそうです。
このように一杯の文書に入っている単語は当然重要とはいえません。

逆に、$2000$個の文章のうち$10$個ぐらいにしか入っていない単語$t$があれば、この単語$t$は重要・文書を特徴づけるといえます。

$idf$の計算式は以下のようになります。

$$idf(t) = log({\frac{総文書数N}{単語tが含まれる文書数c_t + 1}})$$

以上の式において、$N$は集めた文書数、$t$は単語を表します。

今回集めたにじさんじのツイートでは、$83$人いるので$N=83$で、$t$=にじさんじとすれば多分$82$人くらいには含まれているので

$$idf(にじさんじ) = log(83/(82 + 1)) = 0$$
みたいな計算式になります。

よって、より多くの文書に含まれる単語$t$ほど、Inversed・分数を使っているため、逆にidfの値は小さくなります。(分子の文書数は固定されているのに、分母の値が大きくなると、当然値は小さくなる)

また、もし$1$個の文書のみにしか含まれていないのであれば

$$idf(バナナ) = log(83/(1+1))) = 1.61$$

のように大きくなります。

ここで、注意するべきなのが

  • $tf$は文書$d$に関する単語$t$の割合・重要度
  • $idf$は単語$t$の全体における逆文書頻度

であり、$tf$では文書$d$の単語$t$に関するものですが、$idf$は全体の文書を見ているので単語$t$のみしか引数にとらない関数です。
ここの違いが頭に入ると見通しが立つ気がします。

+1の意味

分母の+1は0除算を防ぐために使用します。

tf-idf

ついに、tf-idfについてです。
tfは、文書$d$において単語$d$の重要度・割合を表します。
しかし、tfのみではですのような最もよく出てくる単語が重要視されてしまいます。
そこで、idfという単語$t$のレアリティを求めることで、$tf$をある意味抑制する(ですなど)効果をもたせます。

$tf-idf$の計算式は以下です。

$$tfidf(t,d) = tf(t, d) {\times} idf(t)$$

tf値にそのままidf値をかけたものです。
かなりシンプルに書くことができます。

scikit-learnとMecabでtf-idfを求める

それでは、実際にscikit-learnとMecabでtf-idfを求めてみます。

Mecab

Mecabとは文書や文章を形態素解析して、単語ごとに分かち書きしてくれるライブラリです。
英語なら単語と単語ごとに半角スペースが入っているため単語にすぐ分割できるのですが、日本語は非常に扱いづらい文法であるため、Mecabなどの形態素解析ライブラリで分割してからtf-idf値を求めます。

Mecabの導入は多くの記事で扱われているため、各OSごとに参考にしてみてください。

Mecabで文書を分かち書きする

Mecabをインストールしたあとは

pip install mecab-python3  

というライブラリを使用していきます。

まず、先程tweetsフォルダに入っているすべてのライバーの文書のパスを取り出したいのでpathlibライブラリを使用して取り出します。

import pathlib  
import MeCab  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

'''  
tweets/ギルザレンⅢ世【にじさんじ所属】.txt  
tweets/山神カルタ .txt  
tweets/ベルモンド・バンデラス.txt  
tweets/星川サラにじさんじ.txt  
tweets/エクス・アルビオ【にじさんじ所属】エビオ.txt  
tweets/レヴィ・エリファは歌動画をあげたイ.txt  
tweets/相羽 ういは @22日22時~歌ってみた投稿!.txt  
'''  
for path in tweets_path_list:  
    print(path)  

あとは実際に文書を取得してMecabで分かちをしていきます。


import pathlib  
import MeCab  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  

# corpusに['チャイカのツイート200個連結', '卯月のツイート200個連結']  
for path in tweets_path_list:  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

# corpusに分かち書きをする  
'''  
分かち書きされている  
corpus[0] = 'RT @_ kanade _ kanon : ここ で やっ て ます ! 22222 人 まで ベース を 弾き 続ける 配信 !@ yokato _ ch 舞鶴 クン 、'  
'''  
corpus = [tagger.parse(tweet).strip() for tweet in corpus]  


sklearnでtf値を求める

自然言語処理や科学処理に多く用いられるライブラリscikit-learnを使って、tf値を求めていきます。

from sklearn.feature_extraction.text import CountVectorizer  

tf値を求めるには、上記のCountVectorizerクラスを使用します。
出現する単語の出現頻度をベクトル化してくれるクラスです。

実際に使用してみます。

import pathlib  
import MeCab  
import re  
from sklearn.feature_extraction.text import CountVectorizer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  

for path in tweets_path_list:  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

# token_patternを設定して1文字の単語も処理できるようにする  
count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  

# 学習しながら出現頻度を数えてベクトル化させる  
tf = count_vectorizer.fit_transform(corpus)  

上記のようにすればtf値を計算できます。
ただ、このままだとtfがよくわからないので追加で色々とprintで調べてみます。



tf = count_vectorizer.fit_transform(corpus)  

'''  
  (0, 5391)    18  
  (0, 942)    81  
  (0, 3145)    2  
  (0, 3161)    1  
  (0, 8658)    1  
  (0, 9573)    58  
  (0, 11118)    3  
  (0, 9545)    118  
  (0, 10599)    47  
  (0, 419)    1  
  (0, 15936)    9  
  (0, 8737)    11  
  (0, 19100)    1  
  (0, 7544)    9  
  :    :  
  (86, 5139)    1  
  (86, 23625)    1  
  (86, 17887)    1  
  (86, 17219)    1  
  (86, 16396)    1  
  (86, 15722)    1  
  (86, 17459)    1  
  (86, 16394)    1  
  (86, 12594)    3  
  (86, 17216)    1  
  (86, 21411)    1  
  (86, 23432)    1  
'''  
print(tf)  

どうやら、tfの形は$(87, 24218)$の形になっていてこれは$87$個の文書があり、その$87$個の文書に$24218$だけの単語が存在しているようです。
そして、上記のコメント文のように

$$(文書d, 単語t) \ 文書dにおける単語tの頻度$$というタプルで格納されているようです。


tf = count_vectorizer.fit_transform(corpus)  

'''  
[[ 3  1  0 ...  0  0  0]  
 [ 6  2  0 ...  0  0  0]  
 [ 3  6  1 ...  0  0  0]  
 ...  
 [ 4  0  0 ...  0  0  0]  
 [ 5 16  0 ...  0  0  0]  
 [ 0  5  1 ...  0  0  0]]  
'''  
print(tf.toarray())  

'''  
[3 1 0 ... 0 0 0]  
'''  
print(tf.toarray()[0])  

'''  
24218  
'''  
print(len(tf.toarray()[0]))  

次にtfのtoarray()を調べてみます。
先程のprint(tf)では関係あるもののみを取り出したものですが、
toarray()はどうやら、$87{\times}24218$、つまりすべての文書*総単語の二次元配列を返すようです。
それぞれの配列の中の数字は出現頻度を表しています。

よって、

[3 1 0 ... 0 0 0]  

は、ID0の単語が文書0に3回出現していることを表します。


tf = count_vectorizer.fit_transform(corpus)  
'''  
[ 'ヨロシクタノムゾ', 'ラメ', 'リカ', 'リガリガリガリガリガリガリガリガ', 'ル', 'レナチャンカワイイ', 'レペゼンシッブーヤァ', 'ワァ', 'ワァイ', 'ワーイエル', 'ワワワ', 'ン', 'ンアスカァ', 'ンギギギキ]  
'''  
print(count_vectorizer.get_feature_names())  

get_feature_names()をvectorizerオブジェクトに使うと、すべての学習した単語を返します。
この学習したが大事であり、fit_transoformにコーパスを入れると
コーパスを解析してtfベクトルを計算しながら、vectorizerモデルを学習して作ってくれます
これによって、文書にどれくらい単語が出てくるかを記憶してくれます。


tf = count_vectorizer.fit_transform(corpus)  

'''  
{'rt': 5391, '_': 942, 'kanade': 3145, 'kanon': 3161, 'ここ': 8658, 'で': 9573, 'やっ': 11118, 'て': 9  
'''  
print(count_vectorizer.vocabulary_)  

vectorizerのvocaburary_オブジェクトで、それぞれの単語に値するIDを取得することができます。

ただ、単語IDから単語も取得できたほうが便利なため

import pathlib  
import MeCab  
import re  
from sklearn.feature_extraction.text import CountVectorizer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  

for path in tweets_path_list:  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

# token_patternを設定して1文字の単語も処理できるようにする  
count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  

# 学習しながら出現頻度を数えてベクトル化させる  
tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

'''  
{5391: 'rt', 942: '_', 3145: 'kanade', 3161: 'kanon', 8658: 'ここ', 9573: 'で', 11118: 'や  
'''  
print(reverse_vocabulary)  

のように変更しましょう。

tfidf値を計算する

今度はsklearnでtfidf値を計算します。

tfidfを計算するには

from sklearn.feature_extraction.text import TfidfTransformer  

上記のTfidfTransformクラスを利用します。

実際に使用してみます。

import pathlib  
import MeCab  
import re  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  

for path in tweets_path_list:  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

# token_patternを設定して1文字の単語も処理できるようにする  
count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  

# tfidfをするためのインスタンス生成  
tfidf_transformer = TfidfTransformer()  

# 学習しながら出現頻度を数えてベクトル化させる  
tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

# 学習しながらtfからtfidfを計算してベクトル化させる  
tfidf = tfidf_transformer.fit_transform(tf)  

tfidf値が計算できました。

tfidf値はtf値と同様にprintを行うことができます。
例えば

'''  
  (0, 24000)    0.010507156443320918  
  (0, 23998)    0.04808331936584659  
  (0, 23957)    0.008094356769630582  
  (0, 23919)    0.00496008828135326  
  (0, 23873)    0.009616663873169319  
  (0, 23861)    0.0069724688105677205  
  (0, 23829)    0.010507156443320918  
  (0, 23828)    0.009616663873169319  
  (0, 23821)    0.010507156443320918  
  (0, 23817)    0.005681557095940246  
  (0, 23814)    0.009616663873169319  
...  
  (86, 406)    0.021519893356525172  
  (86, 396)    0.0045322960637048035  
  (86, 383)    0.009803927095129094  
  (86, 382)    0.008548160220605602  
  (86, 332)    0.0046534174429075075  
  (86, 304)    0.01026563325474612  
  (86, 229)    0.004716012275345721  
  (86, 197)    0.008451557312749944  
  (86, 162)    0.011394996637337645  
  (86, 140)    0.008842803662669641  
  (86, 119)    0.007533677728179476  
  (86, 72)    0.013325656091916731  
  (86, 27)    0.0061813671639876664  
  (86, 15)    0.04106253301898448  
  (86, 2)    0.00857733118450389  
  (86, 1)    0.01751748928818495  
'''  
print(tfidf)  

のように$(d, t) \ \ tfidf$のような形式で取得することができます。

試しに卯月コウの特徴的な単語を取り出してみる

試しに、自分の一番好きなライバー卯月コウの最も特徴的な単語を$10$個取り出してみます。

卯月コウのTwitterアカウント名は卯月コウとシンプルなので取り出しやすいのも嬉しいです。

tweetsフォルダには

  • 卯月コウ.txt
  • 勇気 ちひろ.txt

のようなファイルが入っているため、とりあえず卯月コウのコーパス読み込みの順番さえ分かれば良いので

import pathlib  
import MeCab  
import re  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  
uduki_id = -1  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    if str(path) == 'tweets/卯月コウ.txt':  
        uduki_id = i  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

#83  
print(uduki_id)  

となり、卯月は$83$番目の文書のようです。(0-index)

よって、$83$番目の文書を解析していきます。

tfidfはnumpyで出来ているようなので、それも踏まえて少しずつ調べていきます(苦手なので)

import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  
uduki_id = -1  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    if str(path) == 'tweets/卯月コウ.txt':  
        uduki_id = i  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

uduki_tfidf = tfidf[uduki_id]  

'''  
  (0, 24136)    0.007111825486882038  
  (0, 24009)    0.04100340384782789  
  (0, 23920)    0.023975314590430244  
  (0, 23916)    0.010971690719244094  
  (0, 23906)    0.0072975164901658215  
  (0, 23902)    0.011987657295215122  
  (0, 23900)    0.008848631820140186  
  (0, 23881)    0.009234884385985943  
  (0, 23817)    0.0064821114767567665  
  (0, 23811)    0.010971690719244094  
  (0, 174)    0.008848631820140186  
  (0, 131)    0.011987657295215122  
  (0, 0)    0.0056158744953569905  
'''  
print(uduki_tfidf)  

'''  
[0.00711183 0.0410034  0.01422365 ... 0.00884863 0.01198766 0.00561587]  
'''  
print(uduki_tfidf.data)  

'''  
[24136 24009 23953 ...   174   131     0]  
'''  
print(uduki_tfidf.indices)  

'''  
[883 388 732 ... 759 813 592]  
'''  
print(np.argsort(uduki_tfidf.data))  

'''  
[ 592  813  759  767  839  560  910  811  641 1047  964  756  433  677  
  284  908 1081  805  873  180]  
'''  
print(np.argsort(uduki_tfidf.data)[::-1][:20])  

uduki_tfidfで卯月コウのtfidf値を求めます。
そして、uduki_tfidfはndarrayのdata, indicesを持っています。
dataは、uduki_tfidfのifidf値の配列です。
また、indicesは、uduki_tfidfの単語IDの配列です。

tfidfの値の大きいものを使用したいので、numpyのargsortをuduki_tfidf.dataに行います。
ただし、これはitdifの小さい順なので大きい順にするために[::-1]でReverseをします。
また、上位$20$単語にしぼりたいため、[:20]で絞ってあげます。

これによって、
上位$20$件の単語IDがuduki_tfidf.indicesの[ 592 813 759 767 839 560 910 811 641 1047 964 756 433 677
284 908 1081 805 873 180]のインデックスにあることが分かりました。


実際に、単語IDのインデックスを上位順に取得してみます。

tfidf = tfidf_transformer.fit_transform(tf)  

uduki_tfidf = tfidf[uduki_id]  

uduki_tfidf_data = uduki_tfidf.data  
uduki_tfidf_indices = uduki_tfidf.indices  

'''  
[ 592  813  759  767  839  560  910  811  641 1047  964  756  433  677  
  284  908 1081  805  873  180]  
'''  
uduki_desc_tfidf_indices = np.argsort(uduki_tfidf_data)[::-1][:20]  
print(uduki_desc_tfidf_indices)  

'''  
[13905  9545  9911  9826  9226 14711  8304  9573 12619  2609  7070  9947  
 16965 11518 19515  8347   942  9596  8892 21316]  
'''  
uduki_term_ids = uduki_tfidf_indices[uduki_desc_tfidf_indices]  
print(uduki_term_ids)  

後は、この単語IDに合う単語を取り出せば完了です!

import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger('-Owakati')  
uduki_id = -1  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    if str(path) == 'tweets/卯月コウ.txt':  
        uduki_id = i  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

uduki_tfidf = tfidf[uduki_id]  

uduki_tfidf_data = uduki_tfidf.data  
uduki_tfidf_indices = uduki_tfidf.indices  

uduki_desc_tfidf_indices = np.argsort(uduki_tfidf_data)[::-1][:20]  
uduki_term_ids = uduki_tfidf_indices[uduki_desc_tfidf_indices]  

uduki_tfidf_to_array = uduki_tfidf.toarray()[0]  

for term_id in uduki_term_ids:  
    print(term_id, reverse_vocabulary[term_id], uduki_tfidf_to_array[term_id])  

'''  
13905 ネジキ 0.3291507215773228  
9545 て 0.26309659781748795  
9911 の 0.2555795521655597  
9826 に 0.24555682462965545  
9226 た 0.22300568767387077  
14711 ポケモン 0.22298067782369754  
8304 から 0.20546591448603824  
9573 で 0.17539773187832533  
12619 コウ 0.16943095738601685  
2609 hgss 0.15506758732644596  
7070 youtube 0.13178456512305764  
9947 は 0.12027273043085164  
16965 卯月 0.11996924884364471  
11518 ん 0.11776704854687557  
19515 時 0.11534788364908688  
8347 が 0.11526136666289949  
942 _ 0.11025000289494734  
9596 と 0.11025000289494734  
8892 し 0.11025000289494734  
21316 神 0.09926655949042691  
'''  

上位$20$件を取り出してみました。

て・の・に・た・からのような助詞が多く入ってしまったのは痛いです。
原因としては

  • 文書数が少ない
  • 一字を許している
  • Mecabで文書数が少ないなら外すべきだった

といったものがあると思います。

しかし、卯月の特徴である

  • ポケモン
  • hgss
  • 卯月

などが取り出せたのは成功かなと思います! Yay!

コサイン類似度で似ているライバー同士を探す

tf-idfで各メンバーの発言の特徴量tfidf値をベクトルとして取り出しました。
今度はコサイン類似度を使って、このベクトルがどれくらい似ているか?を計算します。

このコサイン類似度を利用することで

  • どのライバーとどのライバーが似ているか?

を多少なりとも分析することができます。

コサイン類似度

2つのベクトル$a, b$の内積は
$a{\cdot}b$で表され、これはどれくらい$a, b$のベクトルがにているか?を表します。

例えば、$a{\cdot}b> 0$なら$a, b$のベクトルは$90$度以内の差以内であるため、値が大きいほど似ていると判定できます。

また、$a {\cdot} b < 0$なら$a, b$のベクトルは正反対側を互いに向いているため、かなり似ていないと判定できます。

今回tfidf値は必ず$0$以上にベクトルの要素単語$t$に関して成り立つため、内積の結果は$0$以上の値になります。

しかし、このままでは問題がありベクトル自体が大きいほど内積も大きくなる傾向にあるため、各ベクトルの大きさで割ってあげましょう。
これがコサイン類似度になります。

よって、ベクトル空間の2つのベクトル$a, b$のコサイン類似度$cos(a, b)$は

$$cos(a, b) = {\frac{a{\cdot}b}{|a||b|}}$$

と表すことができ、$0{\leq}cos(a, b){\leq}1$の値の範囲を取ります。
値が大きいほど2つのベクトルが似ていることを表します。

sklearnでコサイン類似度を実際に求める

sklearnにコサイン類似度の関数が同様にあるため、その関数を利用して実際にライバーの類似度を求めてみます。

cosine_similarityの関数は

from sklearn.metrics.pairwise import cosine_similarity  

で取り出すことができます。

実際に計算をしてみます。
まずは、それぞれのライバーに似ているライバーは誰なのかを判定していきます。


Mecabのアップデート

ここで、一度脇道にそれますが、Mecabの辞書をより良いものに更新したいと思います。
https://www.pytry3g.com/entry/2018/04/24/143934#ipadic%E3%81%A8NEologd%E3%81%AE%E4%BD%B5%E7%94%A8

の記事を参考にhttps://github.com/neologd/mecab-ipadic-neologdをインストールしてください。
この辞書によってライバーのようなより新しい単語に対応できる可能性が高くなります。

例えば、卯月コウのPythonなら辞書を更新して


import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')  
uduki_id = -1  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    if str(path) == 'tweets/卯月コウ.txt':  
        uduki_id = i  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

uduki_tfidf = tfidf[uduki_id]  

uduki_tfidf_data = uduki_tfidf.data  
uduki_tfidf_indices = uduki_tfidf.indices  

uduki_desc_tfidf_indices = np.argsort(uduki_tfidf_data)[::-1][:20]  
uduki_term_ids = uduki_tfidf_indices[uduki_desc_tfidf_indices]  

uduki_tfidf_to_array = uduki_tfidf.toarray()[0]  

for term_id in uduki_term_ids:  
    print(term_id, reverse_vocabulary[term_id], uduki_tfidf_to_array[term_id])  

のように、tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')で指定すると、新しい辞書を利用してよりそれっぽい結果

15827 ネジキ 0.3520704728387863  
10969 て 0.27337632239616905  
11472 の 0.27337632239616905  
10520 た 0.2358540820672831  
9259 から 0.21977312192633197  
24111 神回 0.1877709188473527  
11352 に 0.18225088159744604  
11006 で 0.17957072157395418  
16695 ポケモンhgss 0.15350507813058592  
7716 youtube 0.14096111937226674  
11508 は 0.1259675211041171  
19239 卯月コウ 0.12380094172035627  
9332 が 0.12328736108062525  
11043 と 0.11792704103364154  
1495 _ 0.1152468810101497  
10027 し 0.10720640093967414  
16692 ポケモン 0.10641755655521497  
13097 より 0.10377912105848223  
16693 ポケモンbw 0.10257915041305479  
18488 倒す 0.09921343631642188  

を得ることができます。


sklearnでコサイン類似度を実際に求める・続

まず、誰と誰が似ているかを判別するためにライバー名をファイル名から取り出します。


import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  
from sklearn.metrics.pairwise import cosine_similarity  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger(r'-Owakati')  
# tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')  

liver_names = []  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    liver_names.append(str(path)[7:])  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  
'''  
['ギルザレンⅢ世【にじさんじ所属】.txt', '山神カルタ .txt', 'ベルモンド・バンデラス.txt', '星川サラにじさんじ.txt', 'エクス・アルビオ【にじさんじ所属】エビオ.txt', 'レヴィ・エリファは歌動画をあげたイ.txt', '相羽 ういは @22日22時~歌ってみ  

'''  
print(liver_names)  
exit()  

次にcosine_similarityを計算します。
cosine_similarityにはndarrayを第一引数と第二引数に入れてそれぞれを比べます。

import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  
from sklearn.metrics.pairwise import cosine_similarity  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger(r'-Owakati')  
# tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')  

liver_names = []  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    liver_names.append(str(path)[7:])  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

# コサイン類似度の計算  
cos_sim = cosine_similarity(tfidf, tfidf)  

'''  
(87, 87)  
'''  
print(cos_sim.shape)  

'''  
[1.         0.36745243 0.40020971 0.37687845 0.48679584 0.39296618  
 0.32475449 0.48771599 0.51188008 0.49888395 0.51679007 0.45069295  
 0.47723434 0.51720331 0.47408174 0.36208652 0.36286    0.40850455  
 0.54679115 0.19825312 0.43943665 0.30474529 0.47853026 0.26954621  
 0.36777196 0.46762089 0.4953912  0.33273372 0.35951795 0.4308227  
 0.54916205 0.20109824 0.40566901 0.38025473 0.35000881 0.3704033  
 0.48784765 0.34652005 0.23136281 0.51088413 0.2640043  0.43869407  
 0.49930171 0.4513614  0.5036411  0.44220298 0.35988111 0.38664139  
 0.48583126 0.53387181 0.26670689 0.48447006 0.37856257 0.40240351  
 0.39740397 0.48846556 0.55480488 0.48707063 0.43367839 0.20747899  
 0.35780093 0.34814093 0.54468381 0.45466204 0.42447385 0.39142948  
 0.40888744 0.37546575 0.50044404 0.48978966 0.44661685 0.54723907  
 0.2896436  0.47974467 0.45961492 0.34728054 0.45316658 0.37889109  
 0.45316681 0.53371577 0.44371163 0.44840978 0.51086943 0.44694857  
 0.40217506 0.52305002 0.45204093]  
'''  
print(cos_sim[0])  

'''  
(87,)  
'''  
print(cos_sim[0].shape)  

上記のように$87$人分のtf-idf値が入ったndarrayを第一引数・第二引数に入れて、cosine_similarityを計算します。

返り値はshapeを見ると$(87, 87)$の二次元配列になっているようです。

cos_sim[0]は0番目のライバーが、$0$~$N-1$番目のライバーとどれくらい類似しているかのコサイン類似度の値が入っています。
よって、$i$番目のライバーと$j$番目のライバーの類似度は
cos_sim[i][j]で計算できます。

それでは、一人ずつ誰と誰が似ているのかを見てみましょう。

import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  
from sklearn.metrics.pairwise import cosine_similarity  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
#tagger = MeCab.Tagger(r'-Owakati')  
tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')  

liver_names = []  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    liver_names.append((str(path)[7:])[:-4])  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer(token_pattern=u'(?u)\\b\\w+\\b')  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

# コサイン類似度の計算  
cos_sim = cosine_similarity(tfidf, tfidf)  

# numpyにしておく  
liver_names = np.array(liver_names)  


np.set_printoptions(formatter={'numpystr': lambda x: x + '   '})  
for i in range(cos_sim.shape[0]):  
    # ライバー名  
    liver_name = liver_names[i]  

    # ライバーの類似度配列  
    liver_similarity = cos_sim[i]  

    # 添字ソート大きい順  
    # 自分は除く  
    indices = np.argsort(liver_similarity)[::-1][1:6]  

    print(liver_name + " と似ているのは ", liver_names[indices], '\n')  

まず、numpyの添字指定ができると取り出しやすいのでliver_namesをndarrayにしておきます。

そして$i$番目のライバー名を取り出し、類似度配列を取り出します。
その後、先程と同様に添え字ソートで大きい順にソートし、自分は除きます。(自分とは当然一致するので)

あとはprintをします。

結果は以下のようになります。

ギルザレンⅢ世【にじさんじ所属】 と似ているのは  [雨森 小夜    樋口楓にじさんじ所属    黛 灰@にじさんじ    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお   ]   

山神カルタ  と似ているのは  [フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    葉加瀬 冬雪  Hakase Fuyuki    雨森 小夜  
 アンジュ・カトリーナ    エクス・アルビオ【にじさんじ所属】エビオ   ]   

ベルモンド・バンデラス と似ているのは  [黛 灰@にじさんじ    樋口楓にじさんじ所属    葉加瀬 冬雪  Hakase Fuyuki    ドーラ  
 夢追翔a.k.a. ゆめお   ]   

星川サラにじさんじ と似ているのは  [三枝 明那さえぐさ あきな    フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    春崎エアル@にじさんじ  
 雪城眞尋@にじさんじ所属    文野環にじさんじの野良猫   ]   

エクス・アルビオ【にじさんじ所属】エビオ と似ているのは  [雨森 小夜    アンジュ・カトリーナ    鈴谷アキにじさんじ所属    葉加瀬 冬雪  Hakase Fuyuki  
 樋口楓にじさんじ所属   ]   

レヴィ・エリファは歌動画をあげたイ と似ているのは  [黛 灰@にじさんじ    雨森 小夜    瀬戸美夜子    樋口楓にじさんじ所属    アンジュ・カトリーナ   ]   

相羽 ういは @22日22時~歌ってみた投稿! と似ているのは  [アルス・アルマルにじさんじ    黛 灰@にじさんじ    葉加瀬 冬雪  Hakase Fuyuki    夢追翔a.k.a. ゆめお  
 瀬戸美夜子   ]   

戌亥 とこ と似ているのは  [アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

郡道美玲おさんぽボイス発売中 と似ているのは  [夢追翔a.k.a. ゆめお    瀬戸美夜子    黛 灰@にじさんじ    アンジュ・カトリーナ    樋口楓にじさんじ所属   ]   

ドーラ と似ているのは  [黛 灰@にじさんじ    樋口楓にじさんじ所属    アンジュ・カトリーナ    雨森 小夜    夢追翔a.k.a. ゆめお   ]   

鈴谷アキにじさんじ所属 と似ているのは  [雨森 小夜    アンジュ・カトリーナ    瀬戸美夜子    健屋 花那(すこや かな)    樋口楓にじさんじ所属   ]   

早瀬 走♀♀ と似ているのは  [健屋 花那(すこや かな)    葉加瀬 冬雪  Hakase Fuyuki    黛 灰@にじさんじ  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    アンジュ・カトリーナ   ]   

舞元啓介舞スバ公式グッズ発売決定! と似ているのは  [雨森 小夜    ジョー・力一(りきいち)    樋口楓にじさんじ所属    黛 灰@にじさんじ    アンジュ・カトリーナ   ]   

エリー・コニファー1028-29ポ三家お泊まり初オフコラボ配信《にじさんじ所属》 と似ているのは  [雨森 小夜    アンジュ・カトリーナ    樋口楓にじさんじ所属    鈴谷アキにじさんじ所属  
 鈴鹿詩子 不定期配信&ネタ動画投稿   ]   

剣持刀也にじさんじ と似ているのは  [雨森 小夜    樋口楓にじさんじ所属    黛 灰@にじさんじ    鈴谷アキにじさんじ所属    アンジュ・カトリーナ   ]   

物述有栖JK証明週間 と似ているのは  [瀬戸美夜子    黛 灰@にじさんじ    樋口楓にじさんじ所属    アンジュ・カトリーナ    雨森 小夜   ]   

夕陽リリ と似ているのは  [黛 灰@にじさんじ    ドーラ    樋口楓にじさんじ所属    葉加瀬 冬雪  Hakase Fuyuki  
 アンジュ・カトリーナ   ]   

笹木咲 と似ているのは  [黛 灰@にじさんじ    樋口楓にじさんじ所属    渋谷ハジメ(オワリ)@にじさんじ    瀬戸美夜子    赤羽葉子   ]   

夢追翔a.k.a. ゆめお と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    郡道美玲おさんぽボイス発売中    樋口楓にじさんじ所属   ]   

花畑チャイカwe are にじさんじ!! と似ているのは  [椎名唯華    社築    リゼ・ヘルエスタ    安土桃    ニュイ・ソシエール   ]   

轟京子 と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    瀬戸美夜子    夢追翔a.k.a. ゆめお  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

社築 と似ているのは  [加賀美 ハヤト    本間ひまわりさんじはんにつぼ    安土桃    雨森 小夜    黛 灰@にじさんじ   ]   

矢車りね @ ボイス販売中 と似ているのは  [アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

シェリン・バーガンディ と似ているのは  [早瀬 走♀♀    健屋 花那(すこや かな)    雨森 小夜    瀬戸美夜子  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

ニュイ・ソシエール と似ているのは  [アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子    雨森 小夜    夢追翔a.k.a. ゆめお   ]   

竜胆 尊  Rindou Mikoto と似ているのは  [黛 灰@にじさんじ    鈴鹿詩子 不定期配信&ネタ動画投稿    樋口楓にじさんじ所属    瀬戸美夜子  
 アンジュ・カトリーナ   ]   

フミ にじさんじ所属Vライバー 次回配信22日(火)24:00 と似ているのは  [健屋 花那(すこや かな)    黛 灰@にじさんじ    瀬戸美夜子    夢追翔a.k.a. ゆめお  
 アンジュ・カトリーナ   ]   

葉山舞鈴 と似ているのは  [夢月ロア22日ポケモンX    舞元啓介舞スバ公式グッズ発売決定!    ニュイ・ソシエール    黛 灰@にじさんじ  
 アンジュ・カトリーナ   ]   

家長 むぎにじさんじ と似ているのは  [雨森 小夜    瀬戸美夜子    樋口楓にじさんじ所属    夢追翔a.k.a. ゆめお    アンジュ・カトリーナ   ]   

加賀美 ハヤト と似ているのは  [葉加瀬 冬雪  Hakase Fuyuki    雨森 小夜    エクス・アルビオ【にじさんじ所属】エビオ    樋口楓にじさんじ所属  
 アンジュ・カトリーナ   ]   

アンジュ・カトリーナ と似ているのは  [瀬戸美夜子    黛 灰@にじさんじ    雨森 小夜    夢追翔a.k.a. ゆめお    戌亥 とこ   ]   

遠北 千南 と似ているのは  [黛 灰@にじさんじ    安土桃    樋口楓にじさんじ所属    アンジュ・カトリーナ    雨森 小夜   ]   

夜見れなにじさんじ所属 と似ているのは  [葉加瀬 冬雪  Hakase Fuyuki    加賀美 ハヤト    瀬戸美夜子    雨森 小夜    アンジュ・カトリーナ   ]   

緑仙 と似ているのは  [春崎エアル@にじさんじ    黛 灰@にじさんじ    夢追翔a.k.a. ゆめお    でびでび・でびる月曜22時定期配信  
 矢車りね @ ボイス販売中   ]   

鷹宮リオン と似ているのは  [黛 灰@にじさんじ    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属    アンジュ・カトリーナ    瀬戸美夜子   ]   

リゼ・ヘルエスタ と似ているのは  [戌亥 とこ    アンジュ・カトリーナ    黛 灰@にじさんじ    樋口楓にじさんじ所属    夢追翔a.k.a. ゆめお   ]   

飛鳥ひな と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

童田明治ねむい と似ているのは  [郡道美玲おさんぽボイス発売中    黛 灰@にじさんじ    夢追翔a.k.a. ゆめお    アンジュ・カトリーナ  
 瀬戸美夜子   ]   

雪汝卍 と似ているのは  [エクス・アルビオ【にじさんじ所属】エビオ    健屋 花那(すこや かな)    桜 凛月  SAKURA RITSUKI  
 早瀬 走♀♀    加賀美 ハヤト   ]   

赤羽葉子 と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

月見しずく@ .  .  と似ているのは  [アンジュ・カトリーナ    瀬戸美夜子    黛 灰@にじさんじ    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

宇志海いちご@にじさんじ所属 と似ているのは  [アンジュ・カトリーナ    瀬戸美夜子    夢追翔a.k.a. ゆめお    黛 灰@にじさんじ  
 伏見ガク(20)†にじさんじ所属   ]   

雪城眞尋@にじさんじ所属 と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    瀬戸美夜子    夢追翔a.k.a. ゆめお    雨森 小夜   ]   

成瀬鳴 と似ているのは  [雨森 小夜    樋口楓にじさんじ所属    アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子   ]   

出雲霞にじさんじの学習型AI と似ているのは  [黛 灰@にじさんじ    雨森 小夜    アンジュ・カトリーナ    樋口楓にじさんじ所属    瀬戸美夜子   ]   

春崎エアル@にじさんじ と似ているのは  [夢追翔a.k.a. ゆめお    矢車りね @ ボイス販売中    黛 灰@にじさんじ    アンジュ・カトリーナ  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

黒井しば と似ているのは  [夢追翔a.k.a. ゆめお    郡道美玲おさんぽボイス発売中    黛 灰@にじさんじ    瀬戸美夜子  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

桜 凛月  SAKURA RITSUKI と似ているのは  [雨森 小夜    健屋 花那(すこや かな)    エクス・アルビオ【にじさんじ所属】エビオ    アンジュ・カトリーナ  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

ジョー・力一(りきいち) と似ているのは  [舞元啓介舞スバ公式グッズ発売決定!    雨森 小夜    黛 灰@にじさんじ    樋口楓にじさんじ所属  
 アンジュ・カトリーナ   ]   

鈴鹿詩子 不定期配信&ネタ動画投稿 と似ているのは  [雨森 小夜    樋口楓にじさんじ所属    アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子   ]   

葛葉 と似ているのは  [本間ひまわりさんじはんにつぼ    黛 灰@にじさんじ    椎名唯華    樋口楓にじさんじ所属    赤羽葉子   ]   

える[email protected]にじさんじ所属 と似ているのは  [樋口楓にじさんじ所属    黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお   ]   

椎名唯華 と似ているのは  [郡道美玲おさんぽボイス発売中    樋口楓にじさんじ所属    黛 灰@にじさんじ    アルス・アルマルにじさんじ  
 葉加瀬 冬雪  Hakase Fuyuki   ]   

アルス・アルマルにじさんじ と似ているのは  [黛 灰@にじさんじ    エクス・アルビオ【にじさんじ所属】エビオ    葉加瀬 冬雪  Hakase Fuyuki  
 夢追翔a.k.a. ゆめお    アンジュ・カトリーナ   ]   

ラトナ・プティ《にじさんじ所属》 と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    葉加瀬 冬雪  Hakase Fuyuki    夢追翔a.k.a. ゆめお  
 瀬戸美夜子   ]   

健屋 花那(すこや かな) と似ているのは  [瀬戸美夜子    フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    雨森 小夜    アンジュ・カトリーナ  
 鈴谷アキにじさんじ所属   ]   

雨森 小夜 と似ているのは  [アンジュ・カトリーナ    エリー・コニファー1028-29ポ三家お泊まり初オフコラボ配信《にじさんじ所属》    瀬戸美夜子  
 鈴谷アキにじさんじ所属    樋口楓にじさんじ所属   ]   

小野町春香22日20:00コラボ 23:00歌【にじさんじ所属】 と似ているのは  [雨森 小夜    アンジュ・カトリーナ    雪城眞尋@にじさんじ所属    黛 灰@にじさんじ  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00   ]   

夢月ロア22日ポケモンX と似ているのは  [アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    黛 灰@にじさんじ  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    葉加瀬 冬雪  Hakase Fuyuki   ]   

魔界ノりりむ地頭が良い と似ているのは  [アンジュ・カトリーナ    瀬戸美夜子    夢追翔a.k.a. ゆめお    黛 灰@にじさんじ    赤羽葉子   ]   

町田ちま と似ているのは  [アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    健屋 花那(すこや かな)    樋口楓にじさんじ所属  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00   ]   

本間ひまわりさんじはんにつぼ と似ているのは  [黛 灰@にじさんじ    赤羽葉子    樋口楓にじさんじ所属    瀬戸美夜子    葉加瀬 冬雪  Hakase Fuyuki   ]   

黛 灰@にじさんじ と似ているのは  [瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属    赤羽葉子   ]   

叶@歌ってみた「野狗子」投稿 と似ているのは  [黛 灰@にじさんじ    赤羽葉子    アンジュ・カトリーナ    瀬戸美夜子    渋谷ハジメ(オワリ)@にじさんじ   ]   

御伽原 江良  と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお  
 モイラ@にじさんじ所属の女神   ]   

でびでび・でびる月曜22時定期配信 と似ているのは  [黛 灰@にじさんじ    緑仙    夢追翔a.k.a. ゆめお    春崎エアル@にじさんじ    アンジュ・カトリーナ   ]   

伏見ガク(20)†にじさんじ所属 と似ているのは  [アンジュ・カトリーナ    宇志海いちご@にじさんじ所属    夢追翔a.k.a. ゆめお    瀬戸美夜子  
 黛 灰@にじさんじ   ]   

文野環にじさんじの野良猫 と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    瀬戸美夜子  
 三枝 明那さえぐさ あきな   ]   

モイラ@にじさんじ所属の女神 と似ているのは  [瀬戸美夜子    黛 灰@にじさんじ    樋口楓にじさんじ所属    アンジュ・カトリーナ    雨森 小夜   ]   

月ノ美兎22日20時~配信 と似ているのは  [樋口楓にじさんじ所属    雨森 小夜    アンジュ・カトリーナ    瀬戸美夜子    黛 灰@にじさんじ   ]   

勇気 ちひろ と似ているのは  [鈴谷アキにじさんじ所属    瀬戸美夜子    アンジュ・カトリーナ    黛 灰@にじさんじ    夢追翔a.k.a. ゆめお   ]   

樋口楓にじさんじ所属 と似ているのは  [雨森 小夜    アンジュ・カトリーナ    黛 灰@にじさんじ    月ノ美兎22日20時~配信    瀬戸美夜子   ]   

静凛FF8のしずりん と似ているのは  [樋口楓にじさんじ所属    ギルザレンⅢ世【にじさんじ所属】    月ノ美兎22日20時~配信    雨森 小夜  
 アンジュ・カトリーナ   ]   

渋谷ハジメ(オワリ)@にじさんじ と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    雨森 小夜    樋口楓にじさんじ所属   ]   

神田笑一 と似ているのは  [黛 灰@にじさんじ    郡道美玲おさんぽボイス発売中    瀬戸美夜子  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00    アンジュ・カトリーナ   ]   

鈴原るるボイス販売中 と似ているのは  [エクス・アルビオ【にじさんじ所属】エビオ    雨森 小夜    鈴鹿詩子 不定期配信&ネタ動画投稿    鈴谷アキにじさんじ所属  
 樋口楓にじさんじ所属   ]   

鈴木勝にじさんじ と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    雨森 小夜   ]   

天宮こころまいにち夕方配信中!《にじさんじ所属》 と似ているのは  [アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    葉加瀬 冬雪  Hakase Fuyuki    黛 灰@にじさんじ  
 フミ にじさんじ所属Vライバー 次回配信22日(火)24:00   ]   

愛園 愛美 と似ているのは  [雪城眞尋@にじさんじ所属    雨森 小夜    アンジュ・カトリーナ    黛 灰@にじさんじ    瀬戸美夜子   ]   

瀬戸美夜子 と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    健屋 花那(すこや かな)    雨森 小夜    夢追翔a.k.a. ゆめお   ]   

シスター・クレア と似ているのは  [雨森 小夜    アンジュ・カトリーナ    樋口楓にじさんじ所属    葉加瀬 冬雪  Hakase Fuyuki  
 黛 灰@にじさんじ   ]   

安土桃 と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    夢追翔a.k.a. ゆめお    アンジュ・カトリーナ    樋口楓にじさんじ所属   ]   

森中花咲22(火)20時UNOコラボ と似ているのは  [黛 灰@にじさんじ    樋口楓にじさんじ所属    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお    瀬戸美夜子   ]   

卯月コウ と似ているのは  [黛 灰@にじさんじ    瀬戸美夜子    樋口楓にじさんじ所属    アンジュ・カトリーナ    夢追翔a.k.a. ゆめお   ]   

三枝 明那さえぐさ あきな と似ているのは  [星川サラにじさんじ    春崎エアル@にじさんじ    文野環にじさんじの野良猫    緑仙    夢追翔a.k.a. ゆめお   ]   

葉加瀬 冬雪  Hakase Fuyuki と似ているのは  [黛 灰@にじさんじ    アンジュ・カトリーナ    瀬戸美夜子    夢追翔a.k.a. ゆめお    樋口楓にじさんじ所属   ]   

語部紡 と似ているのは  [雨森 小夜    瀬戸美夜子    黛 灰@にじさんじ    樋口楓にじさんじ所属    アンジュ・カトリーナ   ]   

結果を見るとどうやら特定のライバーがよく似ていると判定されています。
もしかしたら、ライバーのツイートが特有のものだったり、他の人の名前を利用していたりするとこのような結果になるのかもしれません。
ただ、比較的交友関係がありそう?な部分も多少見られるので結果は良しとします(おい)

やはりデータ数$87$では特徴的なキーワードが出しづらいのかもしれません。

おまけ

各ライバーごとに特徴的な単語を求めてみます。

import pathlib  
import MeCab  
import re  
import numpy as np  
from sklearn.feature_extraction.text import CountVectorizer  
from sklearn.feature_extraction.text import TfidfTransformer  

tweets_dir = "tweets"  

tweets_path_obj = pathlib.Path(tweets_dir)  
tweets_path_list = tweets_path_obj.glob("*")  

corpus = []  
tagger = MeCab.Tagger(r'-Owakati -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd')  

liver_names = []  

tweets_path_list = list(tweets_path_list)  
for i in range(len(list(tweets_path_list))):  
    path = tweets_path_list[i]  
    liver_names.append((str(path)[7:])[:-4])  
    tweet = ''  
    with open(str(path), mode='r') as f:  
        for txt in f:  
            txt = txt.strip()  
            tweet += re.sub(r'https?://[\w/:%#\$&\?\(\)~\.=\+\-…]+', "", txt)  
    corpus.append(tweet)  

corpus = [tagger.parse(tweet).strip() for tweet in corpus]  

count_vectorizer = CountVectorizer()  
tfidf_transformer = TfidfTransformer()  

tf = count_vectorizer.fit_transform(corpus)  

vocabulary = count_vectorizer.vocabulary_  
reverse_vocabulary = {val: key for key, val in vocabulary.items()}  

tfidf = tfidf_transformer.fit_transform(tf)  

for i in range(tfidf.shape[0]):  
    liver_tfidf = tfidf[i]  
    liver_tfidf_data = liver_tfidf.data  
    liver_tfidf_indices = liver_tfidf.indices  

    liver_desc_tfidf_indices = np.argsort(liver_tfidf_data)[::-1][:20]  
    liver_term_ids = liver_tfidf_indices[liver_desc_tfidf_indices]  
    liver_tfidf_to_array = liver_tfidf.toarray()[0]  

    print(liver_names[i])  
    for term_id in liver_term_ids:  
        print(reverse_vocabulary[term_id], liver_tfidf_to_array[term_id])  
    print('\n')  

上記のコードでは、toke_patternを消して、一文字系を消してみました。
どうやら「て」「に」のような助詞が多く入るためです。

その結果

シスター・クレア  
クレア 0.6406420132802066  
さん 0.29312739626705164  
描い 0.249692517429839  
まいにち 0.21128088559859995  
dogma 0.16920278210038317  
ます 0.16750136929545809  
です 0.15633461134242754  
シスタークレア 0.12085913007170226  
ちょこっと 0.11291868878365068  
モード 0.11254418888181164  
ほっこり 0.11189999189185076  
から 0.10212535148750125  
ラジオ 0.09692958130343149  
配信 0.08556448367871726  
まし 0.08095899515947141  
聞ける 0.07649733644495658  
シスター 0.07649733644495658  
変わっ 0.07140397721333097  
ください 0.07124479222594716  
アイカツ 0.0677512132701904  


安土桃  
ももちゃん 0.5601790701671422  
安土桃 0.2838725345713205  
あー 0.18937063664084458  
桃ちゃん 0.131018092879071  
メルカリ 0.131018092879071  
から 0.1232353779992101  
aduchi 0.1091817440658925  
momo 0.099928476313448  
見ろ 0.0930505253340464  
サイン 0.08827073764002503  
恋する 0.087345395252714  
yuchan 0.087345395252714  
rt 0.08215691866614006  
wixoss 0.08194961388485246  
カード 0.08194961388485246  
かわいそう 0.08059198147960746  
です 0.07847942814941586  
はやく 0.07624189678777604  
ワンピース 0.07469054268895228  
ない 0.07386299119945022  


森中花咲22(火)20時UNOコラボ  
森中 0.3439899804771878  
花咲 0.260670878989821  
かざ 0.1872487314325983  
にじさんじ 0.18558237848487574  
びじゅつかん 0.16729925153856504  
ます 0.1570578992773665  
meito 0.14495694553228375  
らん 0.14339472015213756  
satsuki 0.13267169179263633  
rt 0.11362186437849536  
生放送 0.10488383600339672  
maymaymay 0.09950376884447726  
7523 0.09950376884447726  
ロリ 0.09938465806794397  
攻め 0.09771117283164246  
クラブ 0.09771117283164246  
より 0.09512604422867892  
から 0.09468488698207947  
bg 0.0929663741604291  
くまさん 0.0929663741604291  


卯月コウ  
ネジキ 0.4612613482631198  
から 0.2879333950227451  
神回 0.24600605240699724  
ポケモンhgss 0.20111302925471908  
youtube 0.18467869642707319  
卯月コウ 0.16219647400060086  
ポケモン 0.13942181864822834  
より 0.1359651007226513  
ポケモンbw 0.13439297207089845  
倒す 0.12998341789964601  
コウ 0.12400191085964213  
配信 0.11938701744845528  
する 0.09944237499919326  
にじさんじ 0.09831872025166906  
ます 0.09589086160636494  
ない 0.08878783482070828  
対決 0.08406054406709496  
さん 0.07813329464222328  
ゲーム実況 0.07764883885147258  
ベストパートナー 0.07687689137718663  

一部切り出しですが、だいぶその人っぽいデータが取れています!
うれしいです!

最後に

はじめて、自然言語処理をやってみました。
だいぶ長くなってしまいましたが、tf-idfはかなりキーワードをスパッととれるなぁと思いました。
ただ、他にもWord2Vec・Doc2Vecという概念があるようなのでそちらも勉強していきたいです。

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

競プロなどをします。

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう
目次をみる
技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
or 外部アカウントではじめる
10秒で技術ブログが作れます!