Подтвердить что ты не робот

Сравнить Python Pandas DataFrames для сопоставления строк

У меня этот DataFrame (df1) в Pandas:

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:\Users\test.py", line 19, in <module>
    print df1.lookup(list1, cols)
  File "C:\python27\lib\site-packages\pandas\core\frame.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:\Users\test.py", line 12, in <module>
    print (df2 == df1).all(1).any()
  File "C:\python27\lib\site-packages\pandas\core\ops.py", line 884, in f
    return self._compare_frame(other, func, str_rep)
  File "C:\python27\lib\site-packages\pandas\core\frame.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

Возможно ли найти набор строк в DataFrame, сравнив его с другими строками данных?

EDIT: Можно ли отбросить строки df2, если эти строки также присутствуют в df1?

4b9b3361

Ответ 1

Одним из возможных решений вашей проблемы будет использование merge. Проверка наличия любой строки (всех столбцов) из другого фрейма данных (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')

РЕДАКТИРОВАТЬ: Новый вопрос (комментарии), идентифицировавший строки из 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'])] не учитывает все типы ситуаций. Рассмотрим следующие DataFrames:

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

Использование вышеуказанных DataFrames с целью удаления строк из 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 и не должна удаляться.

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

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)

Ответ 2

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

ЭТО РАБОТЫ

Столбцы для обоих фреймов всегда одинаковы - A, B, C и D. Имея это в виду, основываясь главным образом на подходах Andrew, вот как удалить строки из 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().