Найти записи, которые не совпадают между столбцами и перебрать столбцы - PullRequest
7 голосов
/ 28 февраля 2020

У меня есть два набора данных, которые мне нужно проверить. Все записи должны совпадать. У меня возникли проблемы с определением, как перебирать каждый отдельный столбец.

import pandas as pd 
import numpy as np

df = pd.DataFrame([['charlie', 'charlie', 'beta', 'cappa'], ['charlie', 'charlie', 'beta', 'delta'], ['charlie', 'charlie', 'beta', 'beta']], columns=['A_1', 'A_2','B_1','B_2'])

df.head()

Out[83]: 
       A_1      A_2   B_1    B_2
0  charlie  charlie  beta  cappa
1  charlie  charlie  beta  delta
2  charlie  charlie  beta   beta

Например, в приведенном выше коде я хочу сравнить A_1 с A_2 и B_1 с B_2, чтобы вернуть новый столбец, A_check и B_check соответственно, которые возвращают True, если A_1 соответствует A_2 как A_Check например.

Примерно так:

df['B_check'] = np.where((df['B_1'] == df['B_2']), 'True', 'False')
df_subset = df[df['B_check']=='False'] 

Но итерируемый по всем заданным именам столбцов, где столбцы, с которыми нужно проверять, всегда будут иметь одинаковое имя перед подчеркиванием и всегда иметь 1 или 2 после подчеркивания.

В конечном счете, фактическая задача имеет несколько фреймов данных с разными столбцами для проверки, а также различное количество столбцов для проверки. Результат, к которому я в конечном итоге обращаюсь, - это фрейм данных, который показывает все записи, которые были ложными для любой конкретной проверки столбца.

Ответы [ 7 ]

6 голосов
/ 03 марта 2020

С немного более полным регулярным выражением:

from itertools import groupby
import re

for k, cols in groupby(sorted(df.columns), lambda x: x[:-2] if re.match(".+_(1|2)$", x) else None):
    cols=list(cols)
    if(len(cols)==2 and k):
        df[f"{k}_check"]=df[cols[0]].eq(df[cols[1]])

Он будет соединять вместе только столбцы, имена которых заканчиваются на _1 и _2 независимо от того, что у вас есть в их именах, вычисляя _check только если есть 2- _1 и _2 (при условии, что у вас нет двух столбцов с одинаковым именем).

Для данных примера:

       A_1      A_2   B_1    B_2  A_check  B_check
0  charlie  charlie  beta  cappa     True    False
1  charlie  charlie  beta  delta     True    False
2  charlie  charlie  beta   beta     True     True
4 голосов
/ 28 февраля 2020

Вы можете использовать wide_to_long, если вам известна первая часть имен столбцов, например A,B ...:

(pd.wide_to_long(df.reset_index(), ['A','B'], 'index','part',sep='_')
   .groupby('index').nunique().eq(1)
   .add_suffix('_check')
)

Вывод:

       A_check  B_check
index                  
0         True    False
1         True    False
2         True     True
3 голосов
/ 03 марта 2020

Вы можете разделить столбцы и сгруппировать по axis=1 на серии первого значения результата разделения и вызвать agg для сравнения

i_cols = df.columns.str.split('_')
df_check = (df.groupby(i_cols.str[0], axis=1).agg(lambda x: x.iloc[:,0] == x.iloc[:,-1])
              .add_suffix('_check'))

In [69]: df_check
Out[69]:
   A_check  B_check
0     True    False
1     True    False
2     True     True
3 голосов
/ 03 марта 2020

Другой способ - использовать изменение формы данных с использованием pd.MultiIndexes:

df = pd.DataFrame([['charlie', 'charlie', 'beta', 'cappa'], 
                   ['charlie', 'charlie', 'beta', 'delta'], 
                   ['charlie', 'charlie', 'beta', 'beta']], 
                  columns=['A_1', 'A_2','B_1','B_2'])

df.columns = df.columns.str.split('_', expand=True) #Creates MultiIndex column header
dfs = df.stack(0) #move the 'A' and 'B' and any others to rows
df_out = (dfs == dfs.shift(-1, axis=1))['1'].unstack() #Compare column 1 to column 2 and move 'A's and 'B's back to columns.
print(df_out)

Вывод:

      A      B
0  True  False
1  True  False
2  True   True
2 голосов
/ 09 марта 2020

Пусть

df = pd.DataFrame([['charlie', 'charlie', 'beta', 'cappa'], ['charlie', 'charlie', 'beta', 'delta'], ['charlie', 'charlie', 'beta', 'beta']], columns=['A_1', 'A_2','B_1','B_2'])

Я всегда был поклонником однострочников, хотя это может быть не очень PEP8. Вот один из способов:

df_check = pd.DataFrame([pd.Series(df[f'{col_root}_1']==df[f'{col_root}_2'], name=f'{col_root}_Check') for col_root in list(set([i.split('_')[0] for i in df.columns]))]).T

df_check:

   B_Check  A_Check
0    False     True
1    False     True
2     True     True
2 голосов
/ 03 марта 2020

Почему бы не сделать что-то простое:

for i in df.columns:
    col=i.split('_1')[0]
    if ('_1' in i)&(col+'_2' in df.columns):
        df[col+'_check']=np.where(df[col+'_1']==df[col+'_2'],True,False)

Я перебираю имена столбцов и проверяю, есть ли у него '_1', и соответствующий столбец с '_2', после чего я используя то же условие np.where, которое вы использовали в вопросе.

Надеюсь, это поможет.

1 голос
/ 06 марта 2020

Здесь я использовал лямбду для итерации каждой строки за раз. Затем выберите столбцы для сравнения, используя оператор if else.

df['A_check'] = df.apply(lambda row: 'True' if row['A_1'] == row['A_2'] else 'False', axis=1)
df['B_check'] = df.apply(lambda row: 'True' if row['B_1'] == row['B_2'] else 'False', axis=1)
print(df)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...