Панды: различие двух датафреймов

Мне нужно сравнить две строки данных разного размера по строкам и распечатать несоответствующие строки. Давайте возьмем следующие два:

df1 = DataFrame({
'Buyer': ['Carl', 'Carl', 'Carl'],
'Quantity': [18, 3, 5, ]})

df2 = DataFrame({
'Buyer': ['Carl', 'Mark', 'Carl', 'Carl'],
'Quantity': [2, 1, 18, 5]})

Каков наиболее эффективный способ построчной обработки по df2 и распечатывания строк не в df1, например:

Buyer     Quantity 
Carl         2
Mark         1

Важно: я не хочу иметь строку:

Buyer     Quantity 
Carl         3

включены в diff:

Я уже пробовал: Сравнение двух строк данных разной длины строки за строкой и добавление столбцов для каждой строки с одинаковым значением и Вывод разницы в двух кадрах данных Pandas бок о бок - выделение разницы

Но они не соответствуют моей проблеме.

Спасибо

Энди

8 ответов

merge 2 dfs, использующие метод 'external' и pass param indicator=True это скажет вам, присутствуют ли строки как только / только слева / только справа, затем вы можете отфильтровать объединенный df после:

In [22]:
merged = df1.merge(df2, indicator=True, how='outer')
merged[merged['_merge'] == 'right_only']

Out[22]:
  Buyer  Quantity      _merge
3  Carl         2  right_only
4  Mark         1  right_only

Вы можете найти это как лучшее:

df2[ ~df2.isin(df1)].dropna()

Ответ @EdChum самоочевиден. Но используяnot 'both'condition имеет больше смысла, и вам не нужно заботиться о порядке сравнения, и это то, чем должен быть настоящий diff. Ради ответа на ваш вопрос:

merged = df1.merge(df2, indicator=True, how='outer')
merged.loc = [merged['_merge'] != 'both']
diff = set(zip(df2.Buyer, df2.Quantity)) - set(zip(df1.Buyer, df1.Quantity))

Это первое решение, которое пришло в голову. Затем вы можете поместить набор различий обратно в DF для презентации.

Начиная с Pandas 1.1.0, есть pandas.DataFrame.compare:

df1.compare(df2)

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.compare.html

Попробуйте следующее, если вам нужно только добавить новых покупателей к другому df:

df_delta=df2[df2['Buyer'].apply(lambda x: x not in df1['Buyer'].values)]

Важный пограничный случай

Рассмотрим следующее, где у вас есть дополнительная повторяющаяся запись во втором фрейме данных. ('Carl', 5)

      df1 = DataFrame({ 'Buyer':    ['Carl', 'Carl', 'Carl'],
                  'Quantity': [   18 ,     3 ,     5 ]  })

df2 = DataFrame({ 'Buyer':    ['Carl', 'Mark', 'Carl', 'Carl', 'Carl'],
                  'Quantity': [    2 ,     1 ,    18 ,     5 ,     5 ]  })

Ответ EdChum даст вам следующее:

      merged = df1.merge(df2, indicator=True, how='outer')
print(merged[merged['_merge'] == 'right_only'])

  Buyer  Quantity      _merge
4  Carl         2  right_only
5  Mark         1  right_only

Как видите, решение игнорирует дополнительное повторяющееся значение, которого в зависимости от того, что вы делаете, следует избегать.

Вот решение, которое, скорее всего, делает то, что вы хотите:

      df1['duplicate_counter'] = df1.groupby(list(df1.columns)).cumcount()
df2['duplicate_counter'] = df2.groupby(list(df2.columns)).cumcount()
merged = df1.merge(df2, indicator=True, how='outer')
merged[merged['_merge'] == 'right_only']

  Buyer  Quantity  duplicate_counter      _merge
3  Carl         2                  0  right_only
4  Mark         1                  0  right_only
5  Carl         5                  1  right_only

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

Например, есть datacompy.Это позволяет экспортировать отчет о сравнении строк, например:

      DataComPy Comparison
--------------------

DataFrame Summary
-----------------

  DataFrame  Columns  Rows
0  original        5     6
1       new        4     5

Column Summary
--------------

Number of columns in common: 4
Number of columns in original but not in new: 1
Number of columns in new but not in original: 0

Row Summary
-----------

Matched on: acct_id
Any duplicates on match values: Yes
Absolute Tolerance: 0.0001
Relative Tolerance: 0
Number of rows in common: 5
Number of rows in original but not in new: 1
Number of rows in new but not in original: 0

Number of rows with some compared columns unequal: 5
Number of rows with all compared columns equal: 0

Column Comparison
-----------------

Number of columns compared with some values unequal: 3
Number of columns compared with all values equal: 1
Total number of values which compare unequal: 7

Columns with Unequal Values or Types
------------------------------------

       Column original dtype new dtype  # Unequal  Max Diff  # Null Diff
0  dollar_amt        float64   float64          1    0.0500            0
1   float_fld        float64   float64          4    0.0005            3
2        name         object    object          2    0.0000            0

Sample Rows with Unequal Values
-------------------------------

       acct_id  dollar_amt (original)  dollar_amt (new)
0  10000001234                 123.45             123.4

       acct_id  float_fld (original)  float_fld (new)
0  10000001234            14530.1555        14530.155
5  10000001238                   NaN          111.000
2  10000001236                   NaN            1.000
1  10000001235                1.0000              NaN

       acct_id name (original)            name (new)
0  10000001234  George Maharis  George Michael Bluth
3  10000001237      Bob Loblaw         Robert Loblaw

Sample Rows Only in original (First 10 Columns)
-----------------------------------------------

       acct_id  dollar_amt           name  float_fld    date_fld
4  10000001238        1.05  Lucille Bluth        NaN  2017-01-01
Другие вопросы по тегам