Комплексная проверка
def compare(old, new):
new_cols = new.columns.difference(old.columns)
del_cols = old.columns.difference(new.columns)
new_indx = new.index.difference(old.index)
del_indx = old.index.difference(new.index)
# Now that we've checked new and deleted rows and columns
# `align` the dataframes and check the values
old, new = old.align(new, 'inner')
I, J = np.where(old.ne(new))
c = old.columns
r = old.index
changes = pd.DataFrame([
[r[i], c[j], old.iat[i, j], new.iat[i, j]]
for i, j in zip(I, J)
], columns=['Row', 'Column', 'Old', 'New'])
return changes, new_cols, del_cols, new_indx, del_indx
Демонстрация
Получить данные изменения
changes, new_cols, del_cols, new_indx, del_indx = compare(df_original, df_new)
Распечатать хороший отчет
print(f"""\
New Columns:
{' '.join(new_cols.astype(str))}
Deleted Columns:
{' '.join(del_cols.astype(str))}
New Rows:
{' '.join(new_indx.astype(str))}
Deleted Rows:
{' '.join(del_indx.astype(str))}
Changes:
{changes}
""")
New Columns:
Money
Deleted Columns:
New Rows:
3
Deleted Rows:
Changes:
Row Column Old New
0 1 Office Name BOS Boston
_______________________________________________________
Более простое решение
Мы можем за go разбить поиск добавленных и удаленных столбцов и строк и просто правильно интерпретировать нулевые значения в changes
кадре данных.
def compare(old, new):
old, new = old.align(new) # Notice I don't use `'inner'` as I did before
I, J = np.where(old.ne(new))
c = old.columns
r = old.index
changes = pd.DataFrame([
[r[i], c[j], old.iat[i, j], new.iat[i, j]]
for i, j in zip(I, J)
], columns=['Row', 'Column', 'Old', 'New'])
return changes
compare(df_original, df_new)
Row Column Old New
0 0 Money NaN 50
1 1 Money NaN 100
2 1 Office Name BOS Boston
3 2 Money NaN 20
4 3 Money NaN 30
5 3 Office Location NaN Los Angeles
6 3 Office Name NaN LA
7 3 Office Number NaN 8
В этом случае единственное изменение представлено ненулевым значением в столбце 'Old'
. Все остальные являются новыми.
_______________________________________________________
Более безопасное решение
В случае, если у вас есть np.nan
как в новом, так и в старом фреймах данных, это будет оцениваться как не равное , Эта версия учитывает это.
Тем не менее, он все равно не поймает, если один кадр данных имеет None
, а другой - np.nan
. Я оставлю это как упражнение для будущих читателей.
def compare(old, new):
old, new = old.align(new)
I, J = np.where(old.ne(new))
c = old.columns
r = old.index
data = []
for i, j in zip(I, J):
n = new.iat[i, j]
o = old.iat[i, j]
if pd.notna(n) or pd.notna(o):
data.append([r[i], c[j], o, n])
return pd.DataFrame(data, columns=['Row', 'Column', 'Old', 'New'])