[PYTHON] Est-il possible d'extraire les informations de profil de la personne du journal de discussion?

Objectif

Environnement et données

API pour l'analyse

Puisqu'il n'y a pas assez de connaissances pour analyser à partir de zéro par moi-même, cette fois NTT Communications a publié API COTOHA A été utilisé.

données brutes

Cette fois, je pense utiliser le journal de discussion comme base. En parlant de chat au Japon, LINE est le courant dominant, mais LINE a une fonction d'exportation de journal de discussion. Cette fois, nous analyserons à l'aide du journal de discussion exporté par LINE. Au cas où, nous avons obtenu le consentement préalable de la personne.

Le fichier était trop gros

Je ne parle généralement pas de choses insipides qui ne touchent pas autant aux profils de l'autre, mais c'était un fichier assez volumineux. Commençons par formater ce fichier.

Bien que ce ne soit pas la vraie chose, le journal de discussion de LINE est structuré comme ceci.

Exemple de fichier original


2019/12/22 Sun
17:00 bowtin [Sticker]
17:01  hogehogekun [Sticker]
17:02 hogehogekun Mangeons des ramen si nous avons du temps libre aujourd'hui

2019/12/23 Mon
05:00 bowtin je suis désolé j'ai dormi
05:00  bowtin [Sticker]
08:35 hogehogekun ne pardonne pas
   :
   :

Premièrement, nous avons éliminé les informations suivantes:

En conséquence, il est devenu comme suit.

Fichier formaté


Si tu es libre aujourd'hui, mangeons des ramen
impardonnable

Puisqu'il s'agit d'une ligne par chat, il est relativement facile à comprendre visuellement. Le nombre de lignes dans le fichier formaté était d'environ 20500.

Divisez le fichier en environ 500 lignes

Au moment du formatage du fichier, il s'agissait d'un journal de discussion assez volumineux avec 20500 lignes. Si j'atteins l'API telle quelle, une erreur est revenue, alors je l'ai divisée en fichiers d'environ 500 lignes chacun. (J'aurais dû utiliser glob ...)

filesplitter.py



with open(file=r'\path\to\file\sample_chatlog.txt', mode='r', encoding='utf-8') as old_file:
    lines = old_file.readlines()

    for i in range(0, 21000, 500):
        line_count = 0 + i
        while line_count <= i + 500:
            with open(file=r'\path\to\file\splitted_file' + str(i) + '.txt', mode='a+', encoding='utf-8') as new_file:
                new_file.write(lines[line_count + i])
                line_count += 1

Je pense qu'il y a une meilleure façon de l'écrire, mais pour le moment, le but était de diviser le fichier, donc je vais l'utiliser.

Estimation des attributs utilisateur à l'aide de l'API COTOHA

L'API COTOHA a plusieurs API ouvertes au public, mais cette fois, nous utiliserons "Estimation des attributs utilisateur". Fait. Cette API est toujours en version bêta (au 19 février 2020).

Maintenant, passons tout le contenu du premier fichier à l'API.

Résultat estimé du premier fichier


{"civilstatus": "Célibataire", "earnings": "1M-3M", "gender": "Femme", "hobby": ["INTERNET", "MUSIC", "PAINT", "TRAVEL", "TVGAME"], "moving": ["BUS", "WALKING"], "occupation": "Étudiant du Collège"},

c'est incroyable. Environ 80% conviennent.

Je vais continuer.

{"civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["COOKING", "FORTUNE", "GOURMET", "INTERNET", "MUSIC", "TRAVEL"], "location": "Kanto", "moving": ["RAILWAY", "WALKING"]},

Cette fois, j'ai obtenu un résultat légèrement différent. Qu'est-ce que "gains": "-1M"? Y a-t-il un moins dans le revenu annuel? ?? Postscript: J'ai reçu un commentaire qui pourrait être interprété comme "0-1M" au lieu de "-1M". C'est peut-être vrai! Cela signifie «moins de 1M» ou «moins de 1M».

De plus, cette fois, il y avait des informations sur la région. Les informations qui peuvent être extraites semblent légèrement différer selon les données d'origine.

Donc, après cela, je viens de passer environ 40 fichiers à l'API. Puisque la réponse ci-dessus vient d'être renvoyée, j'ai essayé de stocker toutes les réponses renvoyées dans un seul fichier.

Voici le code que j'ai réellement utilisé.

main.py


#Informations de base sur les demandes adressées à l'API
BASE_URL = 'https://api.ce-cotoha.com/hogehoge/'
CLIENT_ID = 'YOUR ID'
CLIENT_SECRET = 'YOUR SECRET'
TOKEN_SERVER_URL = 'https://api.ce-cotoha.com/hogehoge/'


#Une fonction qui acquiert un jeton d'accès API (est-ce une spécification que le jeton d'accès est invalidé à intervalles réguliers?...Je suis désolé si c'est différent)
def authorization():
    payload = {
        'grantType': 'client_credentials',
        'clientId': CLIENT_ID,
        'clientSecret': CLIENT_SECRET
    }
    headers = {
        'content-type': 'application/json'
    }
    response = requests.post(TOKEN_SERVER_URL, data=json.dumps(payload), headers=headers)
    auth_info = response.json()

    return auth_info['access_token']


#Une fonction qui fait une requête à l'API (l'argument est une liste de chaînes)
def make_request(original_string_list):
    headers = {
        'Content-Type': 'application/json',
        'charset': 'UTF-8',
        'Authorization': 'Bearer ' + authorization()
    }

    payload = {
        'document': original_string_list,
        'type': 'kuzure' #Il semble qu'il existe un mode pour cela dans le cas de phrases brisées telles que les journaux de discussion
    }

    response = requests.post(BASE_URL, data=json.dumps(payload), headers=headers)

    jsonified_response = response.json()
    return jsonified_response['result']


if __name__ == '__main__':
    #Listez les noms de fichiers d'environ 40 fichiers originaux (cette fois, nous avons une régularité sous forme de nom de fichier + numéro)
    file_list = ['splitted_file' + str(i) + '.txt' for i in range(0, 21000, 500)]

    #Obtenez un nom de fichier dans la liste des noms de fichiers et lisez le contenu
    for a_file in file_list:
        lines = []
        with open(file=(r'path\to\file' + a_file), mode='r', encoding='utf-8') as file:
            lines = file.readlines()
            file.close()
        
        #Jetez le contenu lu à l'API COTOHA tel quel et enregistrez le résultat dans un fichier
        with open(file=r'path\to\file\result.txt', mode='a+', encoding='utf-8') as file:
            file.write(json.dumps(parse(lines)))
            file.close()
            sleep(1) #Si vous lancez trop de demandes en peu de temps, cela causera des problèmes, alors attendez 1 seconde

résultat

C'est pourquoi certains extraits, mais la liste des résultats ressemble à ceci. Même avec le même âge, il existe quelques variantes.

Résultat d'extraction d'attribut utilisateur (extrait partiel).py


[
{"age": "20-29 ans", "civilstatus": "Célibataire", "gender": "Femme", "hobby": ["COOKING", "FORTUNE", "INTERNET", "TVCOMMEDY"], "moving": ["OTHER", "WALKING"]},
{"age": "20-29 ans", "civilstatus": "Célibataire", "gender": "Femme", "hobby": ["GOURMET", "INTERNET", "SMARTPHONE_GAME", "PAINT", "TVGAME"], "moving": ["CYCLING", "OTHER", "RAILWAY", "WALKING"]},
{"civilstatus": "Célibataire", "earnings": "1M-3M", "gender": "Femme", "hobby": ["COOKING", "GOURMET", "INTERNET", "SHOPPING", "TRAVEL"], "moving": ["CYCLING"]},
{"age": "30-39 ans", "civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["ANIMAL", "CAMERA", "COLLECTION", "COOKING", "INTERNET", "SHOPPING"]},
{"age": "20-29 ans", "civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["COOKING", "FORTUNE", "GOURMET", "PAINT"], "location": "Tokai", "moving": ["RAILWAY"]},
{"age": "20-29 ans", "civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["ANIMAL", "DRAMA", "GYM", "SMARTPHONE_GAME", "MUSIC", "TVGAME"], "moving": ["RAILWAY", "WALKING"]}
]

Je ne suis pas sûr que ce soit le cas, alors j'aimerais ajouter. Je coderai en dur le résultat ci-dessus en tant que dict python.

parse_result.py


results = [ 
  {"age": "20-29 ans", "civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["ANIMAL", "DRAMA", "GYM", "SMARTPHONE_GAME", "MUSIC", "TVGAME"], "moving": ["RAILWAY", "WALKING"]},
  {"age": "30-39 ans", "civilstatus": "Célibataire", "earnings": "-1M", "gender": "Femme", "hobby": ["ANIMAL", "CAMERA", "COLLECTION", "COOKING", "INTERNET", "SHOPPING"]}
  #Ce qui suit est omis
]


from collections import Counter
import itertools

#Vous pouvez utiliser des collections pour récupérer les valeurs les plus fréquentes
print(Counter([data['age'] for data in dict_array if 'age' in data]).most_common()[0][0])
print(Counter([data['location'] for data in dict_array if 'location' in data]).most_common()[0][0])
print(Counter([data['gender'] for data in dict_array if 'gender' in data]).most_common()[0][0])
print(Counter([data['civilstatus'] for data in dict_array if 'civilstatus' in data]).most_common()[0][0])
print(Counter([data['earnings'] for data in dict_array if 'earnings' in data]).most_common()[0][0])

#Puisqu'il y a une liste dans la liste, je le jette simplement dans une liste plate, puis je récupère les valeurs les plus fréquentes.
print(Counter(list(itertools.chain.from_iterable([data['hobby'] for data in dict_array if 'hobby' in data]))).most_common()[0][0])

Le résumé des valeurs les plus fréquentes est le suivant.

Valeur la plus fréquente


20-29 ans
Kanto
Femme
Célibataire
1M-3M
INTERNET

Considération

Je pense que la précision est assez élevée. Au moins, je n'aurais pas dû parler de «si je suis marié» dans une conversation avec cette personne, et bien sûr je n'ai pas posé la simple question «Êtes-vous une femme? L'histoire du revenu annuel peut être un peu.

Au fait, j'ai essayé un peu en discutant avec d'autres personnes, mais c'était plutôt correct.

À l'avenir, vous pourrez peut-être trouver le vrai profil de la personne à partir du journal de discussion dans une certaine mesure avec une application correspondante, etc.! Compte tenu des écarts, il semble que les personnes qui utilisent généralement des caractères différents découvriront qu'ils les utilisent correctement.

En conclusion, il s'est avéré que les gens ont renversé leur profil de manière inattendue dans le chat, mais je pense que c'est difficile à comprendre pour ceux qui sont minutieux dans la création de personnages tels que VTuber et le soi-disant nekama.

Recommended Posts

Est-il possible d'extraire les informations de profil de la personne du journal de discussion?
[Dance Dance Revolution] Est-il possible de prédire le niveau de difficulté (pied) à partir de la valeur du radar groove?
Sélectionnez PDFMiner pour extraire les informations textuelles du PDF
Effacez le cron.log régulièrement pour l'empêcher de se développer.
Envoyer les données du journal du serveur vers Splunk Cloud
Ajoutons-le à la variable d'environnement à partir de la commande ~
Est-il possible de détecter des images similaires uniquement avec ImageHash?
Connectez-vous à l'écran de gestion fortigate (6.0) à partir de sélénium-essayez de vous déconnecter
Vous qui coloriez le journal pour le rendre plus facile à voir
Comment se connecter automatiquement comme 1Password depuis CLI
Extraire la valeur la plus proche d'une valeur à partir d'un élément de liste en Python
Pour activer la connexion depuis l'extérieur (autre que localhost) avec dev_appserver.py de GAE / Py
Est-il facile de synthétiser un médicament sur le marché?
Comment extraire la chaîne de caractères souhaitée à partir d'une ligne 4 commandes
Trouvez tous les modèles pour extraire un nombre spécifique de l'ensemble