J'ai les deux ensembles de données suivants - un ensemble de données avec du texte:

text = {'Text':[['Nike', 'invests', 'in', 'shoes'], ['Adidas', 'invests', 'in',  't-shirts']]}
text_df = pd.DataFrame(text)
text_df

Et un ensemble de données avec des mots et des scores et sujets respectifs.

points = {'Text':['invests', 'shoes', 'Adidas'], 'Score':[1, 2, 1], 'Topic':['not_name', 'not_name', 'name' ] }
points_df = pd.DataFrame(points)
points_df

Pour chaque ligne de l'ensemble de données texte, je voudrais voir si le mot existe et, si le mot est là, créer une colonne nommée d'après la catégorie et créer une nouvelle liste avec le score du mot concerné. Si le mot n'est pas là, attribuez un zéro.

C'est le résultat

text_results = {'Text':[['Nike', 'invests', 'in', 'shoes'], ['Adidas', 'invests', 'in',  't-shirts']], 'not_name': [[0, 1, 0, 2], [0, 1, 0, 0]], 'name': [[0, 0, 0, 0], [1, 0, 0, 0]]}
results_df = pd.DataFrame(text_results)
results_df

Aucune suggestion? Je suis un peu perdu en mer!

3
Filippo Sebastio 4 juin 2020 à 08:29

4 réponses

Meilleure réponse

Premièrement, les valeurs en points_df pivotant par DataFrame.pivot_table, remplacement des valeurs manquantes et création du dictionnaire par DataFrame.to_dict:

df1 = points_df.pivot_table(index='Text',
                            columns='Topic',
                            values='Score', 
                            fill_value=0, 
                            aggfunc='sum')
d = df1.to_dict('index')
print (d)
{'Adidas': {'name': 1, 'not_name': 0}, 
 'invests': {'name': 0, 'not_name': 1}, 
 'shoes': {'name': 0, 'not_name': 2}}

À partir des noms de colonnes, un dictionnaire est créé avec des valeurs 0 utilisées pour des valeurs inexistantes:

missd = dict.fromkeys(df1.columns, 0)
print (missd)
{'name': 0, 'not_name': 0}

Ensuite, pour chaque valeur de liste dans text_df['Text'] sont mappées les valeurs par dict.get, donc si aucune correspondance n'est possible, utilisez le dictionnaire de valeurs manquantes par défaut:

L = [[d.get(y, missd) for y in x] for x in text_df['Text']]

Ensuite, changez le format de la liste des dictionnaires au dict des listes dans la compréhension de la liste par cette solution:

L = [{k: [dic[k] for dic in x] for k in x[0]} for x in L]
print (L)
[{'name': [0, 0, 0, 0], 'not_name': [0, 1, 0, 2]}, 
 {'name': [1, 0, 0, 0], 'not_name': [0, 1, 0, 0]}]

Le dernier est créé DataFrame et ajouté à text_df:

df = text_df.join(pd.DataFrame(L, index=text_df.index))
print (df)
                              Text          name      not_name
0       [Nike, invests, in, shoes]  [0, 0, 0, 0]  [0, 1, 0, 2]
1  [Adidas, invests, in, t-shirts]  [1, 0, 0, 0]  [0, 1, 0, 0]
1
jezrael 4 juin 2020 à 07:21

Une autre solution utilisant df.reindex

Créez une fonction personnalisée. Tout d'abord, définissez 'Text' comme index à l'aide de df.set_index, puis en les utilisant df.reindex. Utilisant désormais df.where extraire la colonne 'Score''Topic' est not_name et name, convertissez-les en liste ou en tableau NumPy pd.Series.tolist ou pd.Series.to_numpy() Puis en utilisant df.join les rejoindre.

points_df.set_index('Text',inplace=True)
def func(x):
    x = points_df.reindex(x)
    m = x['Score'].where(x['Topic']=='not_name',0).to_numpy()
    n = x['Score'].where(x['Topic']=='name',0).to_numpy()
    return pd.Series([n,m],index=['name','not_name'])

t = text_df['Text'].apply(func)

text_df.join(t) # or df.merge(t,left_index=True,right_index=True)
                              Text                  name              not_name
0       [Nike, invests, in, shoes]  [0.0, 0.0, 0.0, 0.0]  [0.0, 1.0, 0.0, 2.0]
1  [Adidas, invests, in, t-shirts]  [1.0, 0.0, 0.0, 0.0]  [0.0, 1.0, 0.0, 0.0]
1
Ch3steR 4 juin 2020 à 06:51

D'abord, il vaudrait mieux indexer les points_df en utilisant la colonne Text

points_df.set_index('Text', inplace=True)

Ensuite, nous créons le résultat res dataframe en copiant text_df et en créant des colonnes séparées pour tous les sujets

res = text_df.copy()
for category in list(points_df['Topic'].unique()):
    res[category] = res['Text']

for i in range(len(res)):
    for j in res.columns[1:]:
        res.at[i, j] = [0] * len(res.loc[i,'Text'])

La logique ci-dessous consiste à modifier les valeurs de la liste selon vos besoins


for i in range(len(res)):
    l = res.loc[i]

    for i,word in enumerate(l['Text']):
        if word in list(points_df.index):
            cat = points_df.loc[word]['Topic']
            l[cat][i] = points_df.loc[word, 'Score']

Enfin, le dataframe res est comme ci-dessous:

    Text    not_name    name
0   [Nike, invests, in, shoes]  [0, 1, 0, 2]    [0, 0, 0, 0]
1   [Adidas, invests, in, t-shirts] [0, 1, 0, 0]    [1, 0, 0, 0]
0
Suraj Subramanian 4 juin 2020 à 06:02

Juste une autre façon d'utiliser explode et merge:

s =  text_df.explode("Text").reset_index().merge(points_df, on="Text", how="left").set_index("index").fillna(0)

print (s.assign(Score=np.where(s["Topic"].eq("name"),0,s["Score"]))
        .replace({"Topic":{"not_name":0, "name":1}})
        .rename(columns={"Score":"not_name","Topic":"name"})
        .groupby(level=0).agg(list))

                                  Text              not_name          name
index                                                                     
0           [Nike, invests, in, shoes]  [0.0, 1.0, 0.0, 2.0]  [0, 0, 0, 0]
1      [Adidas, invests, in, t-shirts]  [0.0, 1.0, 0.0, 0.0]  [1, 0, 0, 0]
1
Henry Yik 4 juin 2020 à 06:33