Рассмотрим эти две 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]
спасибо за замечание, я исправил. Постараюсь в следующий раз быть внимательнее @wjandrea

Это сработало для меня:
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)
Вы можете использовать пользовательскую функцию и индексацию:
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
Во-первых, я рекомендую использовать список строк в столбце «Число» 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
Спасибо! Я допустил ошибку (на самом деле ошибки) при публикации вопроса, действительно должны быть списки. Извините за недопонимание
Столбец
Numberвdf2, который вы предоставили, представляет собой строку, а не список. Правильно ли, что это строка, и если да, то является ли столбецIdsв желаемом выводе также строкой? В заголовке и тегах указан список, но приведенный вами пример представляет собой строку с квадратными скобками.