7
votes

Filtrer les lignes du dataframe pandas si une valeur sur une liste à l'intérieur du dataframe se trouve dans une autre liste

J'ai un pandas dataframe qui contient une liste dans la colonne split_categories:

df[~(df['split_categories'].anyvalue.isin([480, 9, 104]))]

Je voudrais sélectionner toutes les lignes où la au moins une catégorie dans un liste spécifique [480, 9, 104].

Résultat attendu:

def match_categories(row):
    selected_categories =  [480, 9, 104]
    result = [int(i) for i in row['split_categories'] if i in selected_categories]
    return result

df['matched_categories'] = df.apply(match_categories, axis=1)

J'arrive à le faire en utilisant apply :

  album_id categories split_categories
0    66562    480.494       [480, 494]
3     1709          9              [9]
4    59239    105.104       [105, 104]

Mais ce code fonctionne en production et cela prend trop de temps (je l'exécute pour plusieurs colonnes contenant des listes)

Existe-t-il un moyen de l'exécuter quelque chose comme:

df.head()

      album_id categories split_categories
    0    66562    480.494       [480, 494]
    1   114582        128            [128]
    2     4846          5              [5]
    3     1709          9              [9]
    4    59239    105.104       [105, 104]

Merci


1 commentaires

Quelle est la taille maximale d'une liste dans df ['split_categories'] , par exemple est-ce toujours 1 ou 2 éléments?


5 Réponses :


1
votes

Utilisation :

  album_id categories split_categories
0    66562    480.494       [480, 494]
3     1709          9              [9]
4    59239    105.104       [105, 104]

Sortie :

print(df[~(df['split_categories'].isin([480, 9, 104])).any()])


0 commentaires

3
votes

Vous pouvez convertir chaque liste en ensembles, obtenir l'intersection et la convertir en booléen:

df = df[mask]
print (df)
  album_id categories split_categories
0    66562    480.494       [480, 494]
3     1709          9              [9]
4    59239    105.104       [105, 104]

Ou convertir la colonne de liste en DataFrame , cast pour flotter et comparer avec isin:

df1 = pd.DataFrame(df['split_categories'].values.tolist(), index=df.index)
mask = df1.astype(float).isin(L).any(axis=1)

L = [480, 9, 104]
mask = np.array([bool(set(map(int, x)) & set(L))  for x in df['split_categories']])


0 commentaires

2
votes

Vous pouvez développer la liste interne et vérifier si des éléments dans les listes internes sont contenus dans [480, 9, 104] :

l = [480, 9, 104]
df[df.categories.str.split('.', expand=True).isin(map(str,l)).any(axis=1)]

   album_id  categories split_categories
0     66562     480.494        [480,494]
3      1709       9.000              [9]
4     59239     105.104        [105,104]


2 commentaires

df.split_categories.str.strip ('[]') renvoie un tableau de NaN (la valeur à l'intérieur de split_categories est déjà une liste (pas un trsing) J'ai utilisé df [df.categories.str.split ('.', expand = True) .isin (map (str, l)). Any (axis = 1)] à la place et cela a fonctionné. Merci


Ohh je vois, j'ai compris que vous deviez utiliser split_categories à la place, j'ai mis à jour la réponse



1
votes

Éviter une série de listes

Vous pouvez diviser en plusieurs séries numériques puis utiliser des opérations booléennes vectorisées. Les boucles au niveau Python utilisant des opérations par ligne sont généralement moins efficaces.

df = pd.DataFrame({'album_id': [66562, 114582, 4846, 1709, 59239],
                   'categories': ['480.494', '128', '5', '9', '105.104']})

split = df['categories'].str.split('.', expand=True).add_prefix('split_').astype(float)
df = df.join(split)

print(df)
#    album_id categories  split_0  split_1
# 0     66562    480.494    480.0    494.0
# 1    114582        128    128.0      NaN
# 2      4846          5      5.0      NaN
# 3      1709          9      9.0      NaN
# 4     59239    105.104    105.0    104.0

L = [480, 9, 104]
res = df[df.filter(regex='^split_').isin(L).any(1)]

print(res)
#    album_id categories  split_0  split_1
# 0     66562    480.494    480.0    494.0
# 3      1709          9      9.0      NaN
# 4     59239    105.104    105.0    104.0


0 commentaires

1
votes

Une autre méthode:

pat = '|'.join(r"\b{}\b".format(x) for x in my_list)
df[df.split_categories.astype(str).str.contains(pat,na=False)]

    album_id    categories  split_categories
0   66562       480.494     [480, 494]
3   1709        9.000       [9]
4   59239       105.104     [105, 104]

Ou:

my_list = [480, 9, 104]
pat = r'({})'.format('|'.join(str(i) for i in my_list))
#'(480|9|104)' <-- This is how the pat looks like
df.loc[df.split_categories.astype(str).str.extract(pat, expand=False).dropna().index]

Cela fonctionnera à la fois avec split_categories et Colonne catégories .


4 commentaires

str.contains ici c'est mieux


@jezrael j'avais essayé, j'ai reçu l'avertissement UserWarning: Ce modèle a des groupes de correspondance. Pour obtenir les groupes, utilisez str.extract. :(


essayez pat = '|' .join (r "\ b {} \ b" .format (x) pour x dans L)


@jezrael qui fonctionne, merci. :) ajoutera dans edit. :) apprend encore le formatage des chaînes. :RÉ