Нахождение любого из элементов существует между двумя столбцами df - PullRequest
3 голосов
/ 07 ноября 2019

У меня есть столбец df содержит количество целых чисел. Я хочу сравнить их и найти любой из существующих элементов ч / б с двумя столбцами и пометить «да», иначе «Нет» в новом столбце, а число совпадает в другом столбце.

INPUT_df:

 A                                          B
[131, 138, 137, 140, 141, 142,35]           [137,35]
[11, 18, 59, 33, 61, 12, 13, 15, 60, 16]    [137,33,16]
[21, 22, 23, 24, 25, 26, 27, 28, 29]        [20,19,33]

Желаемый OUTPUT_df:

 A                                          B             NOTE NO_MATCHED    
[131, 138, 137, 140, 141, 142, 35]          [137,35] yes 137,35
[11, 18, 59, 33, 61, 12, 13, 15, 60, 16]    [137,33,16]  yes 33,16 
[21, 22, 23, 24, 25, 26, 27, 28, 29]        [20,19,33]    No 

Я попытался зациклить на df, но я хотел бы иметь лучший способ:

Это способ, которым я пытался:

save_p = []
for idx,row in INPUT_df.iterrows():
    for i in row.A:
        for j in row.B:
            if int(i) == int(j) :
                save_p.append((row.A,row.B,'yes','i','j'))
            else:
                save_p.append((row.A,row.B,,'No,'i','j))

Может ли pd.isin работать?

Ответы [ 3 ]

1 голос
/ 07 ноября 2019

Вы можете использовать DataFrame.apply для поиска дубликатов по строкам, преобразуя оба списка в серию с помощью pd.Series. Затем
Вы выбираете дубликаты с помощью callable с помощьюSeries.duplicated. Наконец, вы можете join сделать это и создать столбец NOTE, проверяющий, нет ли дубликатов при выходе numpy.where:

df['NOT MATCH']=df[['A','B']].apply(lambda x: ','.join(pd.Series(x.sum()).loc[lambda x: x.duplicated()].astype(str).dropna()),axis=1)
df['NOTE']=np.where(df['NOT MATCH'].eq('')|df['NOT MATCH'].isnull(),'No','Yes')
print(df)

                                          A              B NOT MATCH NOTE
0        [131, 138, 137, 140, 141, 142, 35]      [137, 35]    137,35  Yes
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137, 35, 16]        16  Yes
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20, 19, 33]             No
1 голос
/ 07 ноября 2019

Метод 1: list comprehension & zip:

Мы можем использовать list comprehension с zip здесь для сравнения строк по элементам:

df['NOTE'] = [any([x in a for x in b]) for a, b in zip(df['A'], df['B'])]

df['NO_MATCH'] = [[x for x in b if x in a] for a, b in zip(df['A'], df['B'])]
                                          A              B   NOTE   NO_MATCH
0        [131, 138, 137, 140, 141, 142, 35]      [137, 35]   True  [137, 35]
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137, 33, 16]   True   [33, 16]
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20, 19, 33]  False         []

Метод2: intersection with set():

df['NOTE'] = [any(set(a) & set(b)) for a, b in zip(df['A'], df['B'])]

df['NO_MATCH'] = [list(set(a) & set(b)) for a, b in zip(df['A'], df['B'])]
                                          A              B   NOTE   NO_MATCH
0        [131, 138, 137, 140, 141, 142, 35]      [137, 35]   True  [137, 35]
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137, 33, 16]   True   [16, 33]
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20, 19, 33]  False         []

Пересечение :


Примечание 1 : Если вы хотите yes, no вместо True, False, используйте Series.map:

df['NOTE'] = df['NOTE'].map({True: 'yes', False: 'no'})
                                          A              B NOTE   NO_MATCH
0        [131, 138, 137, 140, 141, 142, 35]      [137, 35]  yes  [137, 35]
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137, 33, 16]  yes   [16, 33]
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20, 19, 33]   no         []

Примечание 2: Плюс, если вы хотите NO_MATCH как str вывод:

df['NO_MATCH'] = df['NO_MATCH'].astype(str).str.replace('[\[\]]', '')
                                          A              B NOTE NO_MATCH
0        [131, 138, 137, 140, 141, 142, 35]      [137, 35]  yes  137, 35
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137, 33, 16]  yes   16, 33
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20, 19, 33]   no         

Примечание3 : Если ваш список является строковым представлением списка, используйте ast.literal_eval:

print(df)
                                          A            B
0         [131, 138, 137, 140, 141, 142,35]     [137,35]
1  [11, 18, 59, 33, 61, 12, 13, 15, 60, 16]  [137,33,16]
2      [21, 22, 23, 24, 25, 26, 27, 28, 29]   [20,19,33]
type(df['A'].iat[0])
# Out: str
from ast import literal_eval

df['A'] = df['A'].apply(literal_eval)
df['B'] = df['B'].apply(literal_eval)

type(df['A'].iat[0])
# Out: list
0 голосов
/ 07 ноября 2019

Если вы предпочитаете иметь функции фильтра (повторное использование метода @ Эрфана):

def match(cols):
    return [[x for x in b if x in a] for a, b in cols]

df['NO_MATCH'] = df[['A', 'B']].apply(match, axis = 1)
df['NOTE'] = df['NO_MATCH'].apply(any)

Возможно более быстрая match функция:

def match(cols):
    return np.array(cols[0])[np.in1d(*cols)].tolist()

вы также можете assume_unique = True чтобы ускорить это еще больше, если оба списка имеют только уникальные значения

def match(cols):
    return np.array(cols[0])[np.in1d(*cols, assume_unique = True)].tolist()
...