J'ai un ensemble de données pandas appelé tf qui a une colonne contenant des mots-clés séparés par des espaces vides intitulé "Mots-clés":

Name         ...                    Keywords
0  Jonas 0         ...                Archie Betty
1  Jonas 1         ...                      Archie
2  Jonas 2         ...          Chris Betty Archie
3  Jonas 3         ...                 Betty Chris
4  Jonas 4         ...                       Daisy
5  Jonas 5         ...                         NaN
6  Jonas 5         ...                Chris Archie

En entrée, je veux fournir un ensemble de chaînes pour filtrer les lignes par ces mots clés. J'ai pensé à utiliser une liste:

list = ["Chris", "Betty"]

J'ai découvert que je peux filtrer les lignes si je fais de la liste une chaîne avec les entrées séparées par "|":

t="|".join(list)

Et recherchez les correspondances dans cette colonne avec:

tf[tf["Keywords"].str.contains(t, na=False)]

Cela filtre en trouvant N'IMPORTE QUEL contenu correspondant, donc la sortie est:

Name         ...                    Keywords
0  Jonas 0         ...                Archie Betty
2  Jonas 2         ...          Chris Betty Archie
3  Jonas 3         ...                 Betty Chris
6  Jonas 5         ...                Chris Archie

Ce que je veux à la place, c'est:

  1. filtrage en contenant UNIQUEMENT les entrées de la liste et

  2. filtrage en contenant AU MOINS les entrées de la liste

Pour 1. le résultat doit être

3 Jonas 3 ... Betty Chris

Pour 2. le résultat devrait être:

2  Jonas 2         ...          Chris Betty Archie
3  Jonas 3         ...                 Betty Chris

J'ai découvert que ce qui suit a essentiellement fait l'affaire pour 2.

a = tf["Keywords"].str.contains("Chris")
b = tf["Keywords"].str.contains("Betty")
tf[a&b]

Cependant, je dois faire cela de manière générique car la longueur de la liste et ses entrées peuvent varier. J'ai eu une idée maladroite avec une boucle pour intersecter chacune des deux entrées de liste consécutives mais cela n'a pas fonctionné:

i = 0
while i < len(list)-1:
    a = tf["Keywords"].str.contains(list[i])
    b = tf["Keywords"].str.contains(list[i+1])
    tf = a & b
    i += 1

J'apprécie ton aide.

6
Jonas 20 nov. 2018 à 14:57

4 réponses

Meilleure réponse

Remarque:

N'utilisez pas le nom de variable list, car le mot de code python.


Solution si tous les mots clés n'ont qu'un seul mot, pas d'espace entre:

Vous pouvez diviser tous les mots par un espace et les convertir en set, afin de les comparer par ensemble converti à partir de la liste L:

L = ["Chris", "Betty"]
s = set(L)

arr = np.array([set(x.split()) if isinstance(x, str) else set([]) for x in tf["Keywords"]])
print (arr)
[{'Archie', 'Betty'} {'Archie'} {'Chris', 'Archie', 'Betty'}
 {'Chris', 'Betty'} {'Daisy'} set() {'Chris', 'Archie'}]

df1 = tf[arr == s]
print (df1)
      Name     Keywords
3  Jonas 3  Betty Chris

df2 = tf[arr >= s]
print (df2)
      Name            Keywords
2  Jonas 2  Chris Betty Archie
3  Jonas 3         Betty Chris

Solution plus générale fonctionnant avec plusieurs mots dans les mots clés:

print (tf)
      Name                  Keywords
0  Jonas 0              Archie Betty
1  Jonas 1                    Archie
2  Jonas 2        Chris Betty Archie
3  Jonas 3               Betty Chris
4  Jonas 4  Daisy Chris Archie Betty
5  Jonas 5                       NaN
6  Jonas 5        Chris Archie Betty

L = ["Chris Archie", "Betty"]
s = set(L)

#create pattern with word boundaries
pat = '|'.join(r"\b{}\b".format(x) for x in L)

#extract all keywords and convert to sets
a = tf['Keywords'].str.findall('('+ pat + ')')
a = np.array([set(x) if isinstance(x, list) else set([]) for x in a])
#remove all matched keywords and remove possible traling whitespaces
b = tf['Keywords'].str.replace(pat, '').str.strip()

#compare only matched values and also empty value after replace
df1 = tf[(b == '') & (a == s)]
print (df1)
      Name            Keywords
6  Jonas 5  Chris Archie Betty

#same like one keyword solution
df2 = tf[a >= s]
print (df2)
      Name                  Keywords
4  Jonas 4  Daisy Chris Archie Betty
6  Jonas 5        Chris Archie Betty
0
jezrael 20 nov. 2018 à 14:36

Je pense que c'est plus ce que vous recherchez, les cellules de trame de données pandas peuvent en fait contenir des listes:

import pandas

# Create a test dataframe
df = pandas.DataFrame(
    [
        {"name": "A", "keywords": "Something SomethingElse"},
        {"name": "B", "keywords": "SomethingElse Tada"},
        {"name": "C", "keywords": "Something SomethingElse AndAnother"},
    ]
)

# Split the keywords INSIDE the cell
df["keywords"] = df["keywords"].apply(lambda row: row.split(" "))

# Filter for a specific keyword
filter_terms = ["Something"]
filtered = df.loc[df["keywords"].apply(lambda row: any([term in filter_terms for term in row]))]

# Show the filtered results
print(filtered)
0
Gijs Wobben 20 nov. 2018 à 12:10

Ajoutez simplement l'approche que vous avez impliquée dans votre message avec

DataFrame juste simulé:

>>> df
      Name            Keywords
0  Jonas 0        Archie Betty
1  Jonas 1              Archie
2  Jonas 2  Chris Betty Archie
3  Jonas 3         Betty Chris
4  Jonas 4               Daisy
5  Jonas 5                 NaN

Utiliser str.contains tout en utilisant les noms avec | séparés ..

>>> df[df.Keywords.str.contains("Chris|Betty", na=False)]
      Name            Keywords
0  Jonas 0        Archie Betty
2  Jonas 2  Chris Betty Archie
3  Jonas 3         Betty Chris

Maintenant, si nous avons plusieurs recherches de noms, appliquer la recherche de base de modèle en construisant l'expression régulière en joignant les mots de pattern avec |:

>>> pattern
['Chris', 'Betty']

>>> df[df.Keywords.str.contains('|'.join(pattern), na=False)]
      Name            Keywords
0  Jonas 0        Archie Betty
2  Jonas 2  Chris Betty Archie
3  Jonas 3         Betty Chris
0
Karn Kumar 20 nov. 2018 à 16:29
def compset(x, mylist):
    y = set(x.lower().split())
    if len(y.intersection(mylist)) > 1:  # == 2 for exact match
        return True
    else:
        return False

mylist=set('chris betty'.lower().split())

df['Keywords'].apply(compset, args=(mylist,))
0
shantanuo 29 nov. 2018 à 13:25