Ajouter l'ID trouvé dans la liste à la nouvelle colonne dans la trame de données pandas

11

Disons que j'ai le dataframe suivant (une colonne d'entiers et une colonne avec une liste d'entiers) ...

      ID                   Found_IDs
0  12345        [15443, 15533, 3433]
1  15533  [2234, 16608, 12002, 7654]
2   6789      [43322, 876544, 36789]

Et aussi une liste séparée d'ID ...

bad_ids = [15533, 876544, 36789, 11111]

Compte tenu de cela, et en ignorant la df['ID']colonne et tout index, je veux voir si l'un des ID de la bad_idsliste est mentionné dans la df['Found_IDs']colonne. Le code que j'ai jusqu'à présent est:

df['bad_id'] = [c in l for c, l in zip(bad_ids, df['Found_IDs'])]

Cela fonctionne, mais uniquement si la bad_idsliste est plus longue que la trame de données et pour le jeu de données réel, la bad_idsliste sera beaucoup plus courte que la trame de données. Si je mets la bad_idsliste à seulement deux éléments ...

bad_ids = [15533, 876544]

Je reçois une erreur très populaire (j'ai lu de nombreuses questions avec la même erreur) ...

ValueError: Length of values does not match length of index

J'ai essayé de convertir la liste en une série (aucun changement dans l'erreur). J'ai également essayé d'ajouter la nouvelle colonne et de définir toutes les valeurs Falseavant de faire la ligne de compréhension (encore une fois aucun changement dans l'erreur).

Deux questions:

  1. Comment faire fonctionner mon code (ci-dessous) pour une liste plus courte qu'une trame de données?
  2. Comment puis-je obtenir le code pour écrire l'ID réel retrouvé dans la df['bad_id']colonne (plus utile que True / False)?

Sortie attendue pour bad_ids = [15533, 876544]:

      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]    True
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]    True

Sortie idéale pour bad_ids = [15533, 876544](les ID sont écrits dans une ou plusieurs nouvelles colonnes):

      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]    15533
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]    876544

Code:

import pandas as pd

result_list = [[12345,[15443,15533,3433]],
        [15533,[2234,16608,12002,7654]],
        [6789,[43322,876544,36789]]]

df = pd.DataFrame(result_list,columns=['ID','Found_IDs'])

# works if list has four elements
# bad_ids = [15533, 876544, 36789, 11111]

# fails if list has two elements (less elements than the dataframe)
# ValueError: Length of values does not match length of index
bad_ids = [15533, 876544]

# coverting to Series doesn't change things
# bad_ids = pd.Series(bad_ids)
# print(type(bad_ids))

# setting up a new column of false values doesn't change things
# df['bad_id'] = False

print(df)

df['bad_id'] = [c in l for c, l in zip(bad_ids, df['Found_IDs'])]

print(bad_ids)

print(df)
MDR
la source

Réponses:

7

Utiliser np.intersect1dpour obtenir l'intersection des deux listes:

df['bad_id'] = df['Found_IDs'].apply(lambda x: np.intersect1d(x, bad_ids))

      ID                   Found_IDs    bad_id
0  12345        [15443, 15533, 3433]   [15533]
1  15533  [2234, 16608, 12002, 7654]        []
2   6789      [43322, 876544, 36789]  [876544]

Ou avec juste du python vanille en utilisant l'intersection de sets:

bad_ids_set = set(bad_ids)
df['Found_IDs'].apply(lambda x: list(set(x) & bad_ids_set))
Erfan
la source
3

Si vous voulez tester toutes les valeurs des listes en Found_IDscolonne par toutes les valeurs d' bad_idsutilisation:

bad_ids = [15533, 876544]

df['bad_id'] = [any(c in l for c in bad_ids) for l  in df['Found_IDs']]
print (df)
      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]    True
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]    True

Si vous voulez que tous correspondent:

df['bad_id'] = [[c for c in bad_ids if c in l] for l  in df['Found_IDs']]
print (df)
      ID                   Found_IDs    bad_id
0  12345        [15443, 15533, 3433]   [15533]
1  15533  [2234, 16608, 12002, 7654]        []
2   6789      [43322, 876544, 36789]  [876544]

Et pour la première correspondance, si une liste vide est définie False, solution possible, mais pas recommandé de mélanger booléen et nombres:

df['bad_id'] = [next(iter([c for c in bad_ids if c in l]), False) for l  in df['Found_IDs']]
print (df)
      ID                   Found_IDs  bad_id
0  12345        [15443, 15533, 3433]   15533
1  15533  [2234, 16608, 12002, 7654]   False
2   6789      [43322, 876544, 36789]  876544

Solution avec des ensembles:

df['bad_id'] = df['Found_IDs'].map(set(bad_ids).intersection)
print (df)

      ID                   Found_IDs    bad_id
0  12345        [15443, 15533, 3433]   {15533}
1  15533  [2234, 16608, 12002, 7654]        {}
2   6789      [43322, 876544, 36789]  {876544}

Et également similaire avec la compréhension de la liste:

df['bad_id'] = [list(set(bad_ids).intersection(l)) for l  in df['Found_IDs']]
print (df)
      ID                   Found_IDs    bad_id
0  12345        [15443, 15533, 3433]   [15533]
1  15533  [2234, 16608, 12002, 7654]        []
2   6789      [43322, 876544, 36789]  [876544]
jezrael
la source
1

Vous pouvez appliquer et utiliser np.any:

df['bad_id'] = df['Found_IDs'].apply(lambda x: np.any([c in x for c in bad_ids]))

Cela renvoie le bool s'il existe un bad_id dans Found_IDs, si vous voulez récupérer ce bad_ids:

df['bad_id'] = df['Found_IDs'].apply(lambda x: [*filter(lambda x: c in x, bad_ids)])

Cela renverra une liste des bad_ids à found_ids, s'il y a 0, il renvoie []

Bruno Mello
la source
1

en utilisant mergeet concaten regroupant par votre index pour retourner toutes les correspondances.

bad_ids = [15533, 876544, 36789, 11111]

df2 = pd.concat(
    [
        df,
        pd.merge(
            df["Found_IDs"].explode().reset_index(),
            pd.Series(bad_ids, name="bad_ids"),
            left_on="Found_IDs",
            right_on="bad_ids",
            how="inner",
        )
        .groupby("index")
        .agg(bad_ids=("bad_ids", list)),
    ],
    axis=1,
).fillna(False)
print(df2)


      ID                   Found_IDs          bad_ids
0  12345        [15443, 15533, 3433]          [15533]
1  15533  [2234, 16608, 12002, 7654]            False
2   6789      [43322, 876544, 36789]  [876544, 36789]
Datanovice
la source
0

Utiliser l'explosion et le regroupement d'agrégats

s = df['Found_IDs'].explode()
df['bad_ids'] = s.isin(bad_ids).groupby(s.index).any()

Pour bad_ids = [15533, 876544]

>>> df
      ID                   Found_IDs  bad_ids
0  12345        [15443, 15533, 3433]     True
1  15533  [2234, 16608, 12002, 7654]    False
2   6789      [43322, 876544, 36789]     True

OU

Pour obtenir des valeurs correspondant

s = df['Found_IDs'].explode()
s.where(s.isin(bad_ids)).groupby(s.index).agg(lambda x: list(x.dropna()))

Pour bad_ids = [15533, 876544]

      ID                   Found_IDs   bad_ids
0  12345        [15443, 15533, 3433]   [15533]
1  15533  [2234, 16608, 12002, 7654]        []
2   6789      [43322, 876544, 36789]  [876544]
Vishnudev
la source