Agregar ID encontrado en la lista a la nueva columna en el marco de datos de pandas

11

Digamos que tengo el siguiente marco de datos (una columna de enteros y una columna con una lista de enteros) ...

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

Y también una lista separada de ID ...

bad_ids = [15533, 876544, 36789, 11111]

Dado eso, e ignorando la df['ID']columna y cualquier índice, quiero ver si alguno de los ID en la bad_idslista se menciona en la df['Found_IDs']columna. El código que tengo hasta ahora es:

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

Esto funciona pero solo si la bad_idslista es más larga que el marco de datos y para el conjunto de datos real, la bad_idslista será mucho más corta que el marco de datos. Si configuro la bad_idslista a solo dos elementos ...

bad_ids = [15533, 876544]

Me sale un error muy popular (he leído muchas preguntas con el mismo error) ...

ValueError: Length of values does not match length of index

He intentado convertir la lista a una serie (sin cambios en el error). También he intentado agregar la nueva columna y establecer todos los valores Falseantes de hacer la línea de comprensión (nuevamente, no hay cambios en el error).

Dos preguntas:

  1. ¿Cómo hago para que mi código (a continuación) funcione para una lista que es más corta que un marco de datos?
  2. ¿Cómo obtendría el código para escribir la ID real encontrada en la df['bad_id']columna (más útil que Verdadero / Falso)?

Salida esperada para 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

La salida ideal para bad_ids = [15533, 876544](ID (s) se escriben en una nueva columna o columnas):

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

Código:

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
fuente

Respuestas:

7

Utilizando np.intersect1dpara obtener la intersección de las dos listas:

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]

O simplemente con el pitón de vainilla utilizando intersecan de sets:

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

Si desea probar todos los valores de las listas en la Found_IDscolumna por todos los valores de bad_idsuso:

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 quieres todo el partido:

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]

Y para la primera coincidencia, si se establece una lista vacía False, posible solución, pero no se recomienda mezclar booleano y números:

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

Solución con conjuntos:

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}

Y también similar con la comprensión de la lista:

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
fuente
1

Puede aplicar y usar np.any:

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

Esto devuelve el bool si existe un bad_id en Found_IDs, si desea recuperar este bad_ids:

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

Esto devolverá una lista de bad_ids en found_ids, si hay 0 devuelve []

Bruno Mello
fuente
1

usando mergey concatmientras agrupa por su índice para devolver todas las coincidencias.

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
fuente
0

Use explotar y agrupar por agregado

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

por 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

O

Para obtener valores coincidentes

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

por 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
fuente