Tambahkan ID yang ditemukan dalam daftar ke kolom baru di bingkai data panda

11

Katakanlah saya memiliki kerangka data berikut (kolom bilangan bulat dan kolom dengan daftar bilangan bulat) ...

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

Dan juga daftar ID yang terpisah ...

bad_ids = [15533, 876544, 36789, 11111]

Mengingat itu, dan mengabaikan df['ID']kolom dan indeks apa pun, saya ingin melihat apakah ada ID dalam bad_idsdaftar yang disebutkan dalam df['Found_IDs']kolom. Kode yang saya miliki sejauh ini adalah:

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

Ini berfungsi tetapi hanya jika bad_idsdaftar lebih panjang dari kerangka data dan untuk dataset nyata bad_idsdaftar akan jauh lebih pendek daripada kerangka data. Jika saya mengatur bad_idsdaftar hanya dua elemen ...

bad_ids = [15533, 876544]

Saya mendapatkan kesalahan yang sangat populer (saya telah membaca banyak pertanyaan dengan kesalahan yang sama) ...

ValueError: Length of values does not match length of index

Saya telah mencoba mengubah daftar menjadi seri (tidak ada perubahan dalam kesalahan). Saya juga telah mencoba menambahkan kolom baru dan mengatur semua nilai Falsesebelum melakukan garis pemahaman (sekali lagi tidak ada perubahan dalam kesalahan).

Dua pertanyaan:

  1. Bagaimana cara mendapatkan kode saya (di bawah) berfungsi untuk daftar yang lebih pendek dari kerangka data?
  2. Bagaimana saya mendapatkan kode untuk menulis ID aktual yang ditemukan kembali ke df['bad_id']kolom (lebih berguna daripada Benar / Salah)?

Output yang diharapkan untuk 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

Output ideal untuk bad_ids = [15533, 876544](ID) ditulis ke kolom atau kolom baru):

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

Kode:

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
sumber

Jawaban:

7

Menggunakan np.intersect1duntuk mendapatkan persimpangan dari dua daftar:

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]

Atau hanya dengan vanila python menggunakan intersect sets:

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

Jika ingin menguji semua nilai daftar di Found_IDskolom dengan semua nilai bad_idspenggunaan:

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

Jika ingin semua cocok:

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]

Dan untuk pertandingan pertama, jika daftar kosong diatur False, solusi yang mungkin, tetapi tidak disarankan untuk mencampur boolean dan angka:

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

Solusi dengan set:

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}

Dan juga mirip dengan pemahaman daftar:

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

Anda dapat mendaftar dan menggunakan np.any:

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

Ini mengembalikan bool jika ada bad_id di Found_IDs, jika Anda ingin mengambil bad_ids ini:

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

Ini akan mengembalikan daftar bad_ids di found_ids, jika ada 0, ia mengembalikan []

Bruno Mello
sumber
1

menggunakan mergedan concatsementara mengelompokkan berdasarkan indeks Anda untuk mengembalikan semua pertandingan.

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

Gunakan meledak dan kelompokkan dengan agregat

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

Untuk 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

ATAU

Untuk mendapatkan nilai yang cocok

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

Untuk 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
sumber