Help us understand the problem. What is going on with this article?

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

動画リンク

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

はじめに

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

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

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

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

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

まず

  • requests
  • BeautifulSoup

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

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

get_account_list.py
import requests
from bs4 import BeautifulSoup

account_id_list_file_name = "account_id_list.json"
list_url = "https://twitter.com/nijisanji_app/lists/list1/members"

page_content = requests.get(list_url).content
soup = BeautifulSoup(page_content)

print(soup)

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

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

get_account_list.py
import requests
import twitter
import json
from bs4 import BeautifulSoup

account_id_list_file_name = "account_id_list.json"

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

# リストの取得
lists = api.GetLists(screen_name='nijisanji_app')

# 1番目が全員のリスト
all_member_list_id = lists[1].id

# リストIDからメンバー情報を取る
members = api.GetListMembers(list_id=all_member_list_id)

# 各メンバー情報を格納
members_result = []
for member in members:
    member_result = {}
    member_result['id'] = member.id
    member_result['name'] = member.name
    member_result['screen_name'] = member.screen_name
    members_result.append(member_result)

# jsonに出力
with open(account_id_list_file_name, 'w') as f:
    json.dump(members_result, f, ensure_ascii=False)

これで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": "🗼える🗼Elu@にじさんじ所属", "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から、ライバーのツイートを取得していきます。

get_liver_tweets.py
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)

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

tweets/卯月コウ.txt
もうすぐ!

# 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ライブラリを使用して取り出します。

mecab_wakati.py
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で分かちをしていきます。

mecab_wakati.py
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クラスを使用します。
出現する単語の出現頻度をベクトル化してくれるクラスです。

実際に使用してみます。

mecab_wakati.py
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から単語も取得できたほうが便利なため

mecab_wakati.py
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クラスを利用します。

実際に使用してみます。

mecab_wakati.py
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

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

卯月コウ.py
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に合う単語を取り出せば完了です!

卯月コウ.py
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でコサイン類似度を実際に求める・続

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

analyze_simililar_liver.py
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   ] 

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

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

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

🗼える🗼Elu@にじさんじ所属 と似ているのは  [樋口楓🍁にじさんじ所属    黛 灰@にじさんじ    瀬戸美夜子📷💚    アンジュ・カトリーナ⚖    夢追翔🎤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という概念があるようなのでそちらも勉強していきたいです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした