Сравните, находится ли значение из одного фрейма данных в списке значений другого

Рассмотрим эти две dfs:

df1 = pd.DataFrame({
    'Id': {0: 101, 1: 102, 2: 103, 3: 104},
    'Number': {0: 'A1', 1: 'A2', 2: 'B1', 3: 'B1'}})

    Id  Number
0   101 A1
1   102 A2
2   103 B1
3   104 B1

df2 = pd.DataFrame({
    'Client': {0: 'John', 1: 'Mia', 2: 'Claudia'},
    'Number': {0: ['A1', 'B1'], 1: ['Z4'], 2: ['A2']}})

    Client  Number
0   John    [A1, B1]
1   Mia     [Z4]
2   Claudia [A2]

Как проверить, находятся ли значения из df1["Number"] в df2["Number"], и добавить все соответствующие идентификаторы из df1["Id"]? Итак, результаты такие?

    Client  Number   Ids
0   John    [A1, B1] [101, 103, 104]
1   Mia     [Z4]     NaN
2   Claudia [A2]     [102]

Столбец Number в df2, который вы предоставили, представляет собой строку, а не список. Правильно ли, что это строка, и если да, то является ли столбец Ids в желаемом выводе также строкой? В заголовке и тегах указан список, но приведенный вами пример представляет собой строку с квадратными скобками.

Panda Kim 31.05.2024 15:20

спасибо за замечание, я исправил. Постараюсь в следующий раз быть внимательнее @wjandrea

nzskra 31.05.2024 15:56
1
2
82
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Это сработало для меня:

def find_ids(row):
    numbers = row["Number"]
    matches = df1.loc[df1["Number"].isin(numbers)]
    return matches["Id"].tolist() or float('NaN')

df2["Ids"] = df2.apply(find_ids, axis=1)

Вы можете упростить код, перебирая столбец вместо df: def find_ids(numbers): и df2["Number"].apply(find_ids)

wjandrea 31.05.2024 16:16
Ответ принят как подходящий

Вы можете использовать пользовательскую функцию и индексацию:

def mapper(lst, ref):
    idx = [x for x in lst.strip('[]').split(', ') if x in ref.index]
    return ref.loc[idx].tolist() if idx else None

df2['Ids'] = df2['Number'].apply(mapper, ref=df1.set_index('Number')['Id'])

Если ваши входные данные в df2 представляют собой списки (а не строки), упростите:

def mapper(lst, ref):
    idx = [x for x in lst if x in ref.index]
    return ref.loc[idx].tolist() if idx else None

Вариант с использованием Index.intersection (предложено @wjandrea):

def mapper(lst, ref):
    idx = ref.index.intersection(lst)
    return None if idx.empty else ref.loc[idx].tolist()

df2['Ids'] = df2['Number'].apply(mapper, ref=df1.set_index('Number')['Id'])

Выход:

    Client    Number              Ids
0     John  [A1, B1]  [101, 103, 104]
1      Mia      [Z4]             None
2  Claudia      [A2]            [102]

Проще: idx = ref.index.intersection(...) и ... if not idx.empty else None

wjandrea 31.05.2024 16:22

Во-первых, я рекомендую использовать список строк в столбце «Число» df2 вместо одной строки с скобками и запятыми. Если это невозможно, см. обходной путь в конце. Исходные кадры данных будут выглядеть так:

import pandas as pd

df1 = pd.DataFrame(
    {
        'Id': {0: 101, 1: 102, 2: 103, 3: 104},
        'Number': {0: 'A1', 1: 'A2', 2: 'B1', 3: 'B1'},
    }
)
df2 = pd.DataFrame(
    {
        'Client': {0: 'John', 1: 'Mia', 2: 'Claudia'},
        'Number': {0: '[A1, B1]', 1: '[Z4]', 2: '[A2]'},
    }
)

Теперь мы можем создать столбец с пустыми списками и заполнить его идентификаторами. Примечание. Я не утверждаю, что это самое быстрое и удобочитаемое решение. Это просто первый метод, который я нашел.

df2['Ids'] = df2.shape[0] * [[]]

for row, number_lst in df2['Number'].items():
    for number in number_lst:
        df2['Ids'].iloc[row] = (
            df2['Ids'].iloc[row] + df1[df1['Number'] == number]['Id'].to_list()
        )

Результат будет таким:

    Client  Number  Ids
0   John    [A1, B1]    [101, 103, 104]
1   Mia     [Z4]        []
2   Claudia [A2]        [102]

Обратите внимание, что в соответствии с вашим запросом запись Миа отображает пустой список вместо NaN. Вы можете решить эту проблему, включив в конец следующую строку:

import numpy as np

df2.loc[df2['Ids'].isin([[]]), 'Ids'] = np.nan

Альтернативное решение для управления df2 с помощью одной строки, содержащей скобки и запятые, вместо списка строк.

df2 = pd.DataFrame(
    {
        'Client': {0: 'John', 1: 'Mia', 2: 'Claudia'},
        'Number': {0: '[A1, B1]', 1: '[Z4]', 2: '[A2]'},
    }
)

for row, number_lst in df2['Number'].items():
    df2['Number'].iloc[row] = number_lst.strip('[]').replace(" ", "").split(',')

Наконец, вот полный код:

import pandas as pd
import numpy as np

# Original dataframes
df1 = pd.DataFrame(
    {
        'Id': {0: 101, 1: 102, 2: 103, 3: 104},
        'Number': {0: 'A1', 1: 'A2', 2: 'B1', 3: 'B1'},
    }
)
df2 = pd.DataFrame(
    {
        'Client': {0: 'John', 1: 'Mia', 2: 'Claudia'},
        'Number': {0: '[A1, B1]', 1: '[Z4]', 2: '[A2]'},
    }
)

# # Uncomment this to create the second dataframe as I suggested:
# df2 = pd.DataFrame(
#     {
#         'Client': {0: 'John', 1: 'Mia', 2: 'Claudia'},
#         'Number': {0: ['A1', 'B1'], 1: ['Z4'], 2: ['A2']},
#     }
# )

# Coment/remove this if you used the second creation sugestion.
for row, number_lst in df2['Number'].items():
    df2['Number'].iloc[row] = number_lst.strip('[]').replace(" ", "").split(',')

df2['Ids'] = df2.shape[0] * [[]]

for row, number_lst in df2['Number'].items():
    for number in number_lst:
        df2['Ids'].iloc[row] = (
            df2['Ids'].iloc[row] + df1[df1['Number'] == number]['Id'].to_list()
        )

# Coment/remove this if than NaN aren't important, use empty lists instead.
df2.loc[df2['Ids'].isin([[]]), 'Ids'] = np.nan

Спасибо! Я допустил ошибку (на самом деле ошибки) при публикации вопроса, действительно должны быть списки. Извините за недопонимание

nzskra 31.05.2024 17:51

Другие вопросы по теме