[PYTHON] J'ai essayé de visualiser la condition commune des téléspectateurs de la chaîne VTuber

Samari

Parmi les bords qui se connectent entre les distributeurs, le réseau qui n'affiche que les bords avec le poids supérieur de 10% est le suivant, et les téléspectateurs sont souvent vus parmi des distributeurs appartenant au même bureau tels que Nijisanji et hollowive. J'ai découvert que je le portais. graph_author_union_(90, 0).png

Motivé

Actuellement, il existe de nombreux bureaux dans l'industrie VTuber, tels que Nijisanji, hololive, 774 inc., Nori Pro, upd8, etc., et chaque bureau a de nombreux distributeurs. Bien sûr, il existe également des distributeurs individuels qui n'appartiennent pas au bureau. Le distributeur publie des vidéos pendant environ une heure à un rythme d'environ une fois par jour à quelques jours. Si vous additionnez la durée vidéo de chaque bureau, la durée totale des vidéos publiées par jour peut facilement dépasser 24 heures. Par conséquent, il est pratiquement impossible de regarder toutes les vidéos de plusieurs bureaux. Ce qui suit est ma colonne de chaîne enregistrée un jour, mais il est difficile de tout voir. .. .. ある日の登録チャンネル欄.png Peut-être que beaucoup de gens sont dans la même situation. Par conséquent, chaque personne choisit les vidéos et les chaînes en fonction de ses propres goûts et goûts. A ce moment, j'ai pensé que ce serait amusant de visualiser quelles chaînes sont faciles à regarder en même temps, alors j'ai essayé de l'exécuter.

Méthode

De nombreux VTubers diffusent en direct et vous pouvez publier des commentaires pendant la diffusion. Par exemple, si vous lisez la vidéo (https://www.youtube.com/watch?v=Ypc_xKz--fY) de Luna Himemori de hololive (https://www.youtube.com/channel/UCa9Y57gfeY0Zro_noHRVrnw), Vous pouvez voir la section de commentaires suivante au moment de la diffusion en direct de côté. YouTube画面.png Ce journal des commentaires contient la date et l'heure des commentaires, le nom de la visionneuse, des informations sur l'espace, etc. Cette fois, nous utiliserons les informations sur le nom du spectateur pour évaluer les relations entre les canaux. Soit $ U_i $ l'ensemble des téléspectateurs qui ont commenté une chaîne $ i $, et définissons combien les téléspectateurs ont souffert entre les chaînes $ w_ {ij} $, qui est le rapport commun des chaînes $ i $ et $ j . .. $ w_{ij} := \frac{|U_i \cap U_j|}{|U_i \cup U_j|} $$

Ceci est utilisé comme poids de bord lors de la visualisation en tant que réseau.

Calcul et visualisation de la similitude inter-canaux

La période d'acquisition des données va du 1er janvier 2020 au 30 juin 2020. De plus, nous n'avons pas validé si les commentaires de toutes les vidéos avec commentaires ont été obtenus correctement. .. ..

Obtenir les données des commentaires

Il existe une méthode d'acquisition de données dans l'article suivant, alors utilisez-la presque telle quelle.

À titre d'exemple, j'enregistre chaque vidéo au format suivant.

AuthorName BaseDate ChannelId Timestamp VideoId VideoLength
Moss Max 2020-05-07 UC--A2dwZW7-M2kID0N6_lfA 2020-05-07 19:53:23 -Alnw7B1GBo 2953
Choco Corone 2020-05-07 UC--A2dwZW7-M2kID0N6_lfA 2020-05-07 19:54:58 -Alnw7B1GBo 2953
Chien noir 2020-05-07 UC--A2dwZW7-M2kID0N6_lfA 2020-05-07 19:55:08 -Alnw7B1GBo 2953
Oguna 2020-05-07 UC--A2dwZW7-M2kID0N6_lfA 2020-05-07 19:55:56 -Alnw7B1GBo 2953
Vendredi haute tension 2020-05-07 UC--A2dwZW7-M2kID0N6_lfA 2020-05-07 19:56:05 -Alnw7B1GBo 2953

Seul VideoLength est texto. .. .. Je ne vais pas l'utiliser cette fois. .. ..

Créer un jeu de données

Tout d'abord, créez une liste de téléspectateurs qui ont commenté la période de données sur chaque chaîne. Cela peut être fait en fusionnant toutes les listes de commentaires obtenues ci-dessus. Le code ci-dessous essaie de compter le nombre de commentaires, mais c'est pour la commodité d'une autre tâche et n'est pas essentiellement lié à cette tâche.

df = pd.concat([pd.read_pickle(path) for path in comment_paths])
counts = df.groupby(['AuthorName', 'ChannelId', 'VideoId', 'BaseDate']).size().to_frame('Count').reset_index()

Le format est le suivant.

AuthorName ChannelId VideoId BaseDate Count
chro nicle UCwrjITPwG4q71HzihV2C7Nw H7wgvBbxo1U 2020-06-30T00:00:00 1
Fazias UChAnqc_AY5_I3Px5dig3X1Q Q7DS6uaInMA 2020-06-30T00:00:00 26
Yumekui Shuga UCuvk5PilcvDECU7dDZhQiEw 6uiQOEDmD6U 2020-06-30T00:00:00 91
Fatin Thifal UCOmjciHZ8Au3iKMElKXCF_g ZrFJpafDKVw 2020-06-30T00:00:00 3
État de la couette Katatsumuri UC6oDys1BGgBsIC3WhG1BovQ QHTLzahEiX4 2020-06-30T00:00:00 1

Matrice adjacente

Comme mentionné précédemment, cette fois, nous calculerons la couverture du spectateur entre les chaînes. Cela peut être facilement obtenu en créant une liste d'utilisateurs pour chaque canal et en effectuant une opération définie.

def corr_by_author_set_union(counts, channels):
    corr = pd.DataFrame().assign(Channel=channels).set_index('Channel')
    tmp = counts.loc[:, ['ChannelId', 'AuthorName']].drop_duplicates()
    channelId_to_set = {ch: set(tmp[tmp.ChannelId == ch].AuthorName) for ch in channels}
    for  ch1 in channels: 
        corr[ch1] = [(len(channelId_to_set[ch1] & channelId_to_set[ch2]) / \
                    len(channelId_to_set[ch1] | channelId_to_set[ch2])) for ch2 in channels]
    return corr

Représentation du graphique

Maintenant, dessinons le graphique. Le code est presque le même que celui du site suivant.

def create_graph(df, threshold=0.5, is_directed=True):
    assert set(df.index) == set(df.columns)

    #Créer un graphique
    if is_directed:
        graph = nx.DiGraph()
    else:
        graph = nx.Graph()

    #Ajouter un nœud
    for col in df.columns:
        if not graph.has_node(col):
            graph.add_node(col)

    #Ajouter un bord
    for a, b in itertools.combinations(df.columns, 2):
        if a == b or graph.has_edge(a, b):
            continue
        val = df.loc[a, b]
        if abs(val) < threshold:
            continue
        graph.add_edge(a, b, weight=val)

    return graph

def draw_char_graph(G, fname, edge_cmap=plt.cm.Greys, figsize=(16, 8)):
    plt.figure(figsize=figsize)
    weights = [G[u][v]['weight'] for u, v, in G.edges()]
    pos = nx.spring_layout(G, k=16)

    nodes = pos.keys()
    colors = list(set([channel_to_color[n] for n in nodes]))
    color_to_id = {colors[i]: i for i in range(len(colors))}
    angs = np.linspace(0, 2*np.pi, 1+len(colors))
    repos = []
    rad = 3.5
    for ea in angs:
        repos.append(np.array([rad*np.cos(ea), rad*np.sin(ea)]))
    for ea in pos.keys():
        posx = 0
        posx = color_to_id[channel_to_color[ea]]
        pos[ea] += repos[posx]

    nx.draw(G,
            pos, 
            node_color=[channel_to_color[n] for n in G.nodes()],
            edge_cmap=edge_cmap,
            edge_vmin=-3e4,
            width=weights,
            with_labels=True,
            font_family='Yu Gothic',
            font_size=8,
            font_color='green')
    plt.savefig(fname, dpi=128)
    plt.show()

Créez et dessinez un graphique en utilisant ces derniers.

Réseau entier

L'épaisseur du trait correspond au pourcentage élevé de téléspectateurs en commun. .. ..

union_corr = corr_by_author_set_union(channels)
#Il est difficile de comprendre s'il s'agit de ChannelId, alors réécrivez-le en ChannnelName
union_corr = rename_ChannelId_to_ChannelName(union_corr)
graph = create_graph(union_corr, threshold=0, is_directed=False)
draw_char_graph(graph, 'fig/graph_author_union.png', figsize=(16, 16))

«Dans l'ensemble, la direction de Nijisanji fait face, et le pourcentage de personnes qui regardent Nijisanji et d'autres bureaux en même temps est élevé.

graph_author_union.png

Réseau entier (seulement les 10% supérieurs des poids de bord)

Étant donné que le nombre d'affichages dans le graphique précédent est trop grand, envisagez de réduire le nombre d'arêtes. 10% est un sentiment. Puisque seuls les 10% supérieurs sont tracés, si une ligne est tracée ici, il peut être interprété que la couverture du téléspectateur entre les chaînes est très élevée. .. ..

# (Bord e, betweenness_centrality)
pairs = [(90, 0)]
df = union_corr.copy()
for pair in pairs:
    th = np.percentile(df.fillna(0).values.ravel(), pair[0])
    print(pair, th)
    graph = create_graph(df, threshold=th, is_directed=False)
    draw_char_graph(graph , 'fig/graph_author_union_{}.png'.format(pair), figsize=(16, 16))

―― Taux de téléspectateurs élevé dans le même bureau

graph_author_union_(90, 0).png

Réseau de bureaux

Ici, seuls les 10% supérieurs des arêtes sont tracés.   En tant qu'impression personnelle, si la connexion avec le bureau est faible, les éléments suivants peuvent être envisagés.

Réseau interne Nijisanji

«Je ne comprends pas parce que les lignes se chevauchent trop. graph_author_union_btw_Nijisanji Japan_and_Nijisanji Japan.png

3 canaux inférieurs et supérieurs de poids connectés à chaque nœud

index Mean kind
Pêche azuchi 0.02581 Nijisanji Japan
♥ ️ ♠ ️ Déclaration Arisu ♦ ️ ♣ ️ 0.03546 Nijisanji Japan
Gilzaren III Season 2 0.04463 Nijisanji Japan
Akina Saegusa/ Saegusa Akina 0.13969 Nijisanji Japan
Amamiya Kokoro/Kokoro Amamiya [affiliation Nijisanji] 0.14043 Nijisanji Japan
Fille de Gwell Os/Gwelu Os Gar [Nijisanji] 0.14336 Nijisanji Japan

Réseau en hololive

―― Tel que ressenti personnellement ――Il est évident qu'un triangle est formé par la couverture du public. .. .. Je ressens --Nonefure

graph_author_union_btw_Hololive Japan_and_Hololive Japan.png

index Mean kind
Mel Channel Ciel nocturne Mel channel 0.1314 Hololive Japan
SoraCh.Canal Tokinosora 0.1653 Hololive Japan
Nakiri Ayame Ch.Hyakuki Ayame 0.1859 Hololive Japan
Kanata Ch.Kanata Amane 0.2664 Hololive Japan
Watame Ch.Pour enroulement carré 0.2684 Hololive Japan
Shion Ch.Shisaki Zion 0.2699 Hololive Japan

Réseau au sein des holostars

―― Tel que ressenti personnellement --Kaoru Tsukishita est bon

graph_author_union_btw_Holostars_and_Holostars.png

index Mean kind
Izuru Ch.Joueur Izuru 0.1544 Holostars
Kira Ch.Miroir look Kira 0.1688 Holostars
Rikka ch.Ritsumei 0.1748 Holostars
astel ch.Astel 0.2173 Holostars
Shien Ch.Kageyama Cien 0.2178 Holostars
Temma Ch.Kishido Tenshin 0.2222 Holostars

774 Réseau en .inc

-Est-ce que le public est divisé par Sugariri, Hanist et Animare?

graph_author_union_btw_774 inc._and_774 inc..png

index Mean kind
Patra Channel /Suo Patra [Hanist] 0.1307 774 inc.
Haneru Channel /Inaba Haneru [Animare] 0.1335 774 inc.
Chaîne CAMOMI Camomi [Kamomi Camomi] 0.1369 774 inc.
Izumi Channel /Izumi Yuzuhara [Animare] 0.1931 774 inc.
Anna Channel /Anna Torajo [Sugariri] 0.1949 774 inc.
Rene Channel /Rin Ryugasaki [Sugariri] 0.2055 774 inc.

Réseau en upd8

――La ligne est fine et il n'y a pas beaucoup de couverture du public ――La ligne entre la viande de l'oncle Bami est épaisse

graph_author_union_btw_upd8_and_upd8.png

index Mean kind
Moteur Kazumi 0.03281 upd8
Yuuki Channel [Putain d'éducation sexuelle] 0.03323 upd8
Département des retrouvailles Cheri High 0.03345 upd8
Nora Cat Channel 0.04661 upd8
Tomari Mari channel /Lapin Mari-chan 0.04728 upd8
Canal de Magurona 0.04752 upd8

Réseau dans Nori Pro

――Depuis que la ligne disparaît, dessinez les 25% supérieurs de la ligne uniquement ici --Yuzuru Himesaki et Takuma Kumagai n'ont posté aucune vidéo pour le moment.

graph_author_union_btw_Noripuro_and_Noripuro.png

index Mean kind
Chaîne Tsukuda Norio [Tamaki Inuyama] 0.2353 Noripuro
Aimiya Milk Milk Enomiya 0.2453 Noripuro
Shirayuki Mishiro 0.2591 Noripuro
Chaîne Tsukuda Norio [Tamaki Inuyama] 0.2353 Noripuro
Aimiya Milk Milk Enomiya 0.2453 Noripuro
Shirayuki Mishiro 0.2591 Noripuro

Réseau personnel

«J'ai remarqué après avoir comploté, mais Yui Yui et Shia Minase appartiennent au bureau. Il est également évident que les audiences se chevauchent

graph_author_union_btw_Other VTubers_and_Other VTubers.png

index Mean kind
Kobana 0.08867 Other VTubers
Festival de Kazemiya/ Matsuri Channel 0.09249 Other VTubers
Tenso Hiyo 0.09265 Other VTubers
Makio [Individuel] 0.10765 Other VTubers
Minase Shia [Shia Channel] 0.11933 Other VTubers
Yui Yui 〖YouTube〗 0.12053 Other VTubers

Réseau avec un autre bureau

――Si vous faites toutes les combinaisons, il y aura beaucoup d'images, donc seulement entre Nijisanji et hololive «Est-ce l'influence de la famille Ozora que le poids de la carre à laquelle M. Subaru Ozora et M. Keisuke Maimoto sont connectés est plus élevé?

graph_author_union_btw_Hololive Japan_and_Nijisanji Japan.png

Parmi les canaux hololive, le bas 3 de la moyenne pondérale des arêtes connectées à Nijisanji

index Mean kind
Mel Channel Ciel nocturne Mel channel 0.02613 Hololive Japan
SoraCh.Canal Tokinosora 0.03727 Hololive Japan
Towa Ch.Towa éternel 0.03840 Hololive Japan
Kanata Ch.Kanata Amane 0.05254 Hololive Japan
Marine Ch.Cloche au trésor marine 0.05539 Hololive Japan
Subaru Ch.Subaru Ozora 0.05971 Hololive Japan

Parmi les canaux de Nijisanji, les 3 derniers du poids moyen du bord connecté à hololive

index Mean kind
Pêche azuchi 0.003585 Nijisanji Japan
Harusaki Air 0.009090 Nijisanji Japan
Gilzaren III Season 2 0.009123 Nijisanji Japan
[Groupe de 3e année 0] Classe de Mirei Gunmichi 0.085043 Nijisanji Japan
Keisuke Maimoto 0.087824 Nijisanji Japan
Ruru Suzuhara [affiliation Nijisanji] 0.096265 Nijisanji Japan

Impressions

――Si vous améliorez la collaboration, le public sera débordé, c'est vrai ――Il semble intéressant de faire une extraction de noyau et une analyse de cluster.

Recommended Posts

J'ai essayé de visualiser la condition commune des téléspectateurs de la chaîne VTuber
[Python] J'ai essayé de visualiser la relation de suivi de Twitter
J'ai essayé de corriger la forme trapézoïdale de l'image
J'ai essayé de vectoriser les paroles de Hinatazaka 46!
J'ai essayé l'histoire courante de l'utilisation du Deep Learning pour prédire la moyenne Nikkei
J'ai essayé de résumer la forme de base de GPLVM
J'ai essayé de visualiser facilement les tweets de JAWS DAYS 2017 avec Python + ELK
J'ai essayé d'effacer la partie négative de Meros
J'ai essayé de classer les voix des acteurs de la voix
J'ai essayé d'afficher le degré d'infection par le virus corona sur la carte thermique Seaborn
J'ai essayé de résumer les opérations de chaîne de Python
Python pratique 100 coups J'ai essayé de visualiser l'arbre de décision du chapitre 5 en utilisant graphviz
J'ai essayé de visualiser les caractéristiques des nouvelles informations sur les personnes infectées par le virus corona avec wordcloud
[First data science ⑥] J'ai essayé de visualiser le prix du marché des restaurants à Tokyo
J'ai essayé de visualiser les données de course du jeu de course (Assetto Corsa) avec Plotly
J'ai essayé de trouver l'entropie de l'image avec python
[Courses de chevaux] J'ai essayé de quantifier la force du cheval de course
J'ai essayé d'obtenir les informations de localisation du bus Odakyu
[TF] J'ai essayé de visualiser le résultat de l'apprentissage en utilisant Tensorboard
[Apprentissage automatique] J'ai essayé de résumer la théorie d'Adaboost
J'ai essayé de combattre le minimum local de la fonction Goldstein-Price
J'ai essayé de déplacer le ballon
J'ai essayé d'estimer la section.
J'ai essayé de visualiser la consommation électrique de ma maison avec Nature Remo E lite
[Traitement du langage naturel] J'ai essayé de visualiser les remarques de chaque membre de la communauté Slack
[Linux] J'ai essayé de résumer les commandes de confirmation des ressources
J'ai essayé d'obtenir l'index de la liste en utilisant la fonction énumérer
J'ai essayé d'automatiser l'arrosage du pot avec Raspberry Pi
J'ai essayé de créer l'image de démarrage SD de LicheePi Nano
J'ai essayé de visualiser l'ensemble de données de préférence de boisson par décomposition tenseur.
J'ai essayé de visualiser Boeing de la performance du violon par estimation de pose
J'ai essayé de résumer la méthode de mise en œuvre fréquemment utilisée de pytest-mock
J'ai essayé d'améliorer l'efficacité du travail quotidien avec Python
J'ai essayé le serveur asynchrone de Django 3.0
J'ai essayé de résumer la commande umask
J'ai essayé de reconnaître le mot de réveil
J'ai essayé de résumer la modélisation graphique.
J'ai essayé d'estimer le rapport de circonférence π de manière probabiliste
J'ai essayé de toucher l'API COTOHA
[Python] J'ai essayé de visualiser le prix en argent de "ONE PIECE" plus de 100 millions de caractères avec matplotlib.
J'ai essayé de transformer l'image du visage en utilisant sparse_image_warp de TensorFlow Addons
[Python] J'ai essayé de visualiser la nuit du chemin de fer de la galaxie avec WordCloud!
J'ai essayé d'obtenir les résultats de Hachinai en utilisant le traitement d'image
J'ai essayé de transcrire les actualités de l'exemple d'intégration commerciale sur Amazon Transcribe
J'ai essayé d'estimer la similitude de l'intention de la question en utilisant Doc2Vec de gensim
J'ai essayé d'améliorer la précision de mon propre réseau neuronal
J'ai essayé de résoudre 100 traitements linguistiques Knock version 2020 [Chapitre 3: Expressions régulières 25-29]
J'ai essayé d'extraire automatiquement les mouvements des joueurs Wiire avec un logiciel
J'ai essayé de résumer la manière logique de penser l'orientation objet.
J'ai essayé de trouver l'itinéraire optimal du pays des rêves par recuit (quantique)
J'ai essayé d'extraire et d'illustrer l'étape de l'histoire à l'aide de COTOHA
J'ai essayé d'analyser la négativité de Nono Morikubo. [Comparer avec Posipa]
J'ai essayé de rationaliser le rôle standard des nouveaux employés avec Python
J'ai essayé d'obtenir automatiquement le RSS de la chanson la plus populaire de l'iTunes Store
J'ai essayé d'obtenir les informations sur le film de l'API TMDb avec Python
J'ai essayé de visualiser tous les arbres de décision de la forêt aléatoire avec SVG
J'ai essayé d'afficher la valeur d'altitude du DTM dans un graphique
En utilisant COTOHA, j'ai essayé de suivre le cours émotionnel de la course aux meros.