Сравнение фреймов данных Python Pandas для сопоставления строк

у меня есть этот фрейм данных (df1) в панд:

df1 = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
print df1

       A         B         C         D
0.860379  0.726956  0.394529  0.833217
0.014180  0.813828  0.559891  0.339647
0.782838  0.698993  0.551252  0.361034
0.833370  0.982056  0.741821  0.006864
0.855955  0.546562  0.270425  0.136006
0.491538  0.445024  0.971603  0.690001
0.911696  0.065338  0.796946  0.853456
0.744923  0.545661  0.492739  0.337628
0.576235  0.219831  0.946772  0.752403
0.164873  0.454862  0.745890  0.437729

я хотел бы проверить, есть ли строка (все столбцы) из другого фрейма данных (df2) в df1. Вот df2:

df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
print df2

           A         B         C         D
    0.855955  0.546562  0.270425  0.136006
    0.491538  0.445024  0.971603  0.690001
    0.911696  0.065338  0.796946  0.853456
    0.744923  0.545661  0.492739  0.337628
    0.576235  0.219831  0.946772  0.752403
    2.000000  3.000000  4.000000  5.000000
   14.000000 15.000000 16.000000 17.000000

я пробовал использовать df.lookup искать по одной строке за раз. Я сделал это так:

list1 = df2.ix[0].tolist()
cols = df1.columns.tolist()
print df1.lookup(list1, cols)

но я получил это сообщение об ошибке:

  File "C:Userstest.py", line 19, in <module>
    print df1.lookup(list1, cols)
  File "C:python27libsite-packagespandascoreframe.py", line 2217, in lookup
    raise KeyError('One or more row labels was not found')
KeyError: 'One or more row labels was not found'

я тоже пробовал .all() использование:

print (df2 == df1).all(1).any()

но я получил эту ошибку сообщение:

  File "C:Userstest.py", line 12, in <module>
    print (df2 == df1).all(1).any()
  File "C:python27libsite-packagespandascoreops.py", line 884, in f
    return self._compare_frame(other, func, str_rep)
  File "C:python27libsite-packagespandascoreframe.py", line 3010, in _compare_frame
    raise ValueError('Can only compare identically-labeled '
ValueError: Can only compare identically-labeled DataFrame objects

я тоже пробовал isin() такой:

print df2.isin(df1)

но я False везде, что не есть правильно:

    A      B      C      D
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False

можно ли искать набор строк в фрейме данных, сравнивая его с строками другого фрейма данных?

изменить: Есть возможность drop df2 строки, если эти строки также присутствуют в df1?

2 ответов


одним из возможных решений вашей проблемы было бы использование слияние. Проверка наличия какой-либо строки (всех столбцов) из другого фрейма данных (df2) в df1 эквивалентна определению пересечения двух фреймов данных. Это может быть выполнено с помощью следующей функции:

pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner')

например, если df1 был

    A           B            C          D
0   0.403846    0.312230    0.209882    0.397923
1   0.934957    0.731730    0.484712    0.734747
2   0.588245    0.961589    0.910292    0.382072
3   0.534226    0.276908    0.323282    0.629398
4   0.259533    0.277465    0.043652    0.925743
5   0.667415    0.051182    0.928655    0.737673
6   0.217923    0.665446    0.224268    0.772592
7   0.023578    0.561884    0.615515    0.362084
8   0.346373    0.375366    0.083003    0.663622
9   0.352584    0.103263    0.661686    0.246862

и df2 был определен как:

     A          B            C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622
5   2.000000    3.000000    4.000000    5.000000
6   14.000000   15.000000   16.000000   17.000000

функции pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner') производит:

     A           B           C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622

результаты - это все строки (все столбцы), которые находятся как в df1, так и в df2.

мы также можем изменить этот пример, если столбцы не совпадают в df1 и df2, и просто сравнить значения строк, которые одинаковы для подмножества столбцов. Если мы изменим исходный пример:

df1 = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
df2 = df2[['A', 'B', 'C']] # df2 has only columns A B C

затем мы можем посмотреть на общие столбцы, используя common_cols = list(set(df1.columns) & set(df2.columns)) между двумя кадрами данных, то слияние:

pd.merge(df1, df2, on=common_cols, how='inner')

EDIT: новый вопрос (комментарии), определив строки из df2, которые также присутствовали в первом фрейме данных (df1), можно ли взять результат pd.merge () и затем удалить строки из df2, которые также присутствуют в df1

я не знаю простого способа выполнить задачу удаления строк из df2, которые также присутствуют в df1. Тем не менее, вы можете использовать следующий:

ds1 = set(tuple(line) for line in df1.values)
ds2 = set(tuple(line) for line in df2.values)
df = pd.DataFrame(list(ds2.difference(ds1)), columns=df2.columns)

вероятно, существует лучший способ выполнить эту задачу, но я не знаю о таком методе / функции.

EDIT 2: как удалить строки из df2, которые также присутствуют в df1, как показано в ответе @WR.

метод, предусмотренного df2[~df2['A'].isin(df12['A'])] не учитывает все типы ситуаций. Рассмотреть следующее Фреймы данных:

df1:

   A  B  C  D
0  6  4  1  6
1  7  6  6  8
2  1  6  2  7
3  8  0  4  1
4  1  0  2  3
5  8  4  7  5
6  4  7  1  1
7  3  7  3  4
8  5  2  8  8
9  3  2  8  4

df2:

   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8
5  1  1  1  1
6  2  2  2  2

df12:

   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8

использование вышеуказанных фреймов данных с целью удаления строк из df2, которые также присутствуют в df1, приведет к следующему:

   A  B  C  D
0  1  1  1  1
1  2  2  2  2

строки (1, 1, 1, 1) и (2, 2, 2, 2) находятся в df2, а не в df1. К сожалению, используя предоставленный метод (df2[~df2['A'].isin(df12['A'])]) результаты в:

   A  B  C  D
6  2  2  2  2

это происходит потому, что значение 1 в столбце A найдено в обоих пересечениях DataFrame (т. е. (1, 0, 2, 3)) и df2 и, таким образом, удаляет оба (1, 0, 2, 3) и (1, 1, 1, 1). Это непреднамеренно, так как строка (1, 1, 1, 1) не находится в df1 и не должна быть удалена.

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

df12['key'] = 'x'
temp_df = pd.merge(df2, df12, on=df2.columns.tolist(), how='left')
temp_df[temp_df['key'].isnull()].drop('key', axis=1)

@Andrew: я считаю, что нашел способ удалить строки одного фрейма данных, которые уже присутствуют в другом (т. е. ответить на мое редактирование) без использования циклов-дайте мне знать, если вы не согласны и / или если мой OP + EDIT не ясно заявил Это:

ЭТО РАБОТАЕТ

столбцы для обоих фреймов данных всегда одинаковы -A, B, C и D. Имея это в виду, основываясь в значительной степени на подходе Эндрю, вот как отбросить строки из df2, которые также присутствуют в df1:

common_cols = df1.columns.tolist()                         #generate list of column names
df12 = pd.merge(df1, df2, on=common_cols, how='inner')     #extract common rows with merge
df2 = df2[~df2['A'].isin(df12['A'])]

строка 3 делает следующее:

  • извлекать только строки из df2, которые не соответствуют строк df1:
  • для того, чтобы 2 строки были разными, любой столбец одной строки должен
    должен быть тегом в другом ряд.
  • вот, я выбрал колонки A чтобы сделать это сравнение-это
    можно использовать любой из имена столбцов, но не все
    имя столбца.

Примечание: этот метод по существу является эквивалентом SQL NOT IN().