Отчет о разнице / изменении значений между двумя фреймами данных одинаковой формы - PullRequest
0 голосов
/ 06 января 2019

В контексте я хочу сравнить два df и найти разницу.

Вот df и df2 с небольшой разницей:

df = pd.DataFrame({'a': range(3),
                   'b': range(3)})

df2 = df.copy()
df2.iloc[1,1] = 100

Сравнение их дает двухмерный логический df той же формы:

df != df2
Out[28]: 
       a      b
0  False  False
1  False   True
2  False  False

Я пытался извлечь элементы, соответствующие Истине, но другие элементы (которые я не хочу) по-прежнему встречаются как NaN

df[df != df2]
Out[29]: 
    a    b
0 NaN  NaN
1 NaN  1.0
2 NaN  NaN

Как извлечь только элементы, соответствующие истине и индексам (поэтому я знаю, где в df):

df[df != df2] # somehow?
Out[30]: 
    b
1 1.0

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

  1. df маленький, и вы можете захотеть увидеть:

    df = pd.DataFrame({'a': range(3),
                       'b': range(3)})
    
    df2 = df.copy()
    df2.iloc[0,0] = 100
    df2.iloc[1,1] = 100
    
    df[df!=df2].dropna(how='all',axis=(0,1)) # U9-Forward's answer
    Out[39]: 
         a    b
    0  0.0  NaN
    1  NaN  1.0
    
  2. df большой, и вы можете захотеть увидеть:

    index    column   df_value     df2_value
        0         a        0.0           100
        1         b        1.0           100
    

@ U9-Forward хорошо работает для случая 1 и когда есть только один True.

@ coldspeed предоставил комплексное решение. Спасибо!

Ответы [ 5 ]

0 голосов
/ 06 января 2019

Вы можете использовать melt и затем фильтровать для значения, которое отличается:

diffs = df.ne(df2).reset_index().melt(id_vars=['index'])
>>> diffs[diffs['value'].eq(True)].iloc[:, :-1].reset_index(drop=True)
   index variable
0      1        b
0 голосов
/ 06 января 2019

Расколотый раствор:

mask = df2[df!=df2]
a = mask.values

Возвращает массив.

x = a[~np.isnan(a)].astype(int)
>>x
array([100])
0 голосов
/ 06 января 2019

Маска на значения:

df.values[df != df2]
# array([1])

Как следует обращаться с этим делом?

df2.at[0, 'a'] = 100

df
   a  b
0  0  0
1  1  1
2  2  2

df2
     a    b
0  100    0
1    1  100
2    2    2

df != df2 
       a      b
0   True  False
1  False   True
2  False  False

df.values[df != df2]
# array([0, 1])

# in the other answer
df[df!=df2].dropna(how='all',axis=(0,1))
     a    b
0  0.0  NaN
1  NaN  1.0

Какой требуемый выход?


Если вы просто различаете значения в каждом столбце df, то подойдет что-то простое, например agg и dropna.

df[df != df2].agg(lambda x: x.dropna().tolist())

a    [0.0]
b    [1.0]
dtype: object

Если вам нужны индексы и столбцы, используйте melt:

u = df2.reset_index().melt('index')
v = df.reset_index().melt('index')

u[u['value'] != v['value']]
   index variable  value
0      0        a    100
4      1        b    100

Или используйте np.nonzero, чтобы сделать это с numpy - Истинные значения не равны нулю, их индексы возвращаются.

m = (df != df2).values
idx, cols = np.nonzero(m)

pd.DataFrame({
    'index': df.index.values[idx],
    'column': df.columns.values[cols],
    'value_1': df.values[m],
    'value_2': df2.values[m]
})

   index column  value_1  value_2
0      0      a        0      100
1      1      b        1      100
0 голосов
/ 06 января 2019

Если хотите получить информацию об индексе и столбцах, добавьте stack для MultiIndex Series:

df = pd.DataFrame({'a': range(3),
                   'b': range(3)})

df2 = df.copy()
df2.iloc[1,1] = 100
df2.iloc[0,0] = 100

s = df.stack()
s2 = df2.stack()
out = s[s != s2].rename_axis(['idx','col']).reset_index(name='val')
print (out)
   idx col  val
0    0   a    0
1    1   b    1

out2 = s2[s != s2].rename_axis(['idx','col']).reset_index(name='val')
print (out2)
   idx col  val
0    0   a  100
1    1   b  100

Или используйте numpy indexing с numpy.where для индексов значений True s:

mask = df != df2
a = np.where(mask)

out = pd.DataFrame({'idx': df.index.values[a[0]],
                    'cols': df.columns.values[a[1]],
                    'vals_df': df.values[mask],
                    'vals_df2': df2.values[mask]})
print (out)
   idx cols  vals_df  vals_df2
0    0    a        0       100
1    1    b        1       100
0 голосов
/ 06 января 2019

Используйте условие и затем dropna:

print(df[df!=df2].dropna(how='all',axis=(0,1)))
...