检测对数据帧的所有更改



我希望创建一个程序来比较两个数据帧并检测对每个数据帧所做的更改。这适用于版本控制应用程序,其中原始文件已被用户操作,我正在尝试提取他们对原始文件所做的更改。

尝试的幼稚解决方案:

import pandas as pd 

# Initalize the data
data_original = [['4', 'NYC','New York'], ['3', 'BOS','Boston'], ['2', 'CHI','Chicago']]
data_new = [['4', 'NYC','New York','50'], ['3', 'Boston','Boston','100'], ['2', 'CHI','Chicago','20'], ['8', 'LA','Los Angeles','30']] 
# Create the dataframes
df_original = pd.DataFrame(data_original, columns = ['Office Number', 'Office Name','Office Location'])
df_new = pd.DataFrame(data_new, columns = ['Office Number', 'Office Name','Office Location','Money'])
df_changes = df_new[ ~df_new.isin(df_original)].dropna()

生成的数据帧:

['8', 'LA','Los Angeles','30']

这个天真的解决方案不是我想要的,因为它不会检测到数据帧第二项中"BOS"到"Boston"的变化。我正在寻找即使连续更改了单个项目也能指示的东西。列的加减我可以单独计算,但是如何检测元素的变化,例如"波士顿"到"波士顿"?

全面检查

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
__

__

更简单的解决方案

我们可以放弃查找添加和删除的列和行的细分,而只是适当地解释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'列中的非 null 值表示。 所有其他的都是新的。

__

__

更安全的解决方案

如果同时在新旧数据帧中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'])

也许这就是你需要的?

df_changes = df_new[ ~df_new[["Office Number","Office Name","Office Location"]].apply(tuple,1).isin(df_original[["Office Number","Office Name","Office Location"]].apply(tuple,1))].dropna()

不知道现在是否仍然需要 dropna((。您可以将列设置为元组,使其不可变,并且可以跨键进行比较。使用您的测试数据进行测试,我认为它可以工作。

如果比较是沿着索引进行的,那么我们需要两个reindexlike调用。第一个允许将原始数据帧与新数据帧进行比较,而不考虑其他行或列。第二个将所有其他行和列标记为True。生成的数据帧Truedf_newdf_original不同的地方。

m = (df_new.reindex_like(df_original)
.ne(df_original)
.reindex_like(df_new)
.fillna(True))
Office Number  Office Name  Office Location  Money
0          False        False            False   True
1          False         True            False   True
2          False        False            False   True
3           True         True             True   True
# Can slice to see changes
df_new[m]
Office Number Office Name Office Location Money
0           NaN         NaN             NaN    50
1           NaN      Boston             NaN   100
2           NaN         NaN             NaN    20
3             8          LA     Los Angeles    30

相关内容

  • 没有找到相关文章