Python excel差分发生器
我正在尝试创建一个python程序,该程序为我提供了两个包含多个工作表的大型excel文件之间的差异。我让它将结果打印到excel中,但显然,当其中一个单元格包含日期时间数据时,将布尔数据框与包含日期的数据框相乘的操作就不再有效了。我得到以下错误: TypeError:不支持*:“bool”和“datetime.datetime”的操作数类型 “编辑”:我刚刚意识到这个方法对字符串也不起作用(它只对纯数字数据起作用)。对于字符串、数字和时间数据,有什么更好的方法可以做到这一点Python excel差分发生器,python,excel,pandas,Python,Excel,Pandas,我正在尝试创建一个python程序,该程序为我提供了两个包含多个工作表的大型excel文件之间的差异。我让它将结果打印到excel中,但显然,当其中一个单元格包含日期时间数据时,将布尔数据框与包含日期的数据框相乘的操作就不再有效了。我得到以下错误: TypeError:不支持*:“bool”和“datetime.datetime”的操作数类型 “编辑”:我刚刚意识到这个方法对字符串也不起作用(它只对纯数字数据起作用)。对于字符串、数字和时间数据,有什么更好的方法可以做到这一点 #start of
#start of program
import pandas as pd
from pandas import ExcelWriter
import numpy as np
df1 = pd.read_excel('4_Input EfE_2030.xlsm',None)
df2 = pd.read_excel('5_Input EfE_2030.xlsm',None)
keys1=df1.keys()
keys2=df2.keys()
writer = ExcelWriter('test1.xlsx')
#loop for all sheets and create new dataframes with the differences
for x in keys1:
df3 = pd.read_excel('4_Input EfE_2030.xlsm',sheetname=x,header=None)
df4 = pd.read_excel('5_Input EfE_2030.xlsm',sheetname=x,header=None)
dif = df3 != df4
df=dif*df3
df2=dif*df4
nrcolumns=len(df.columns)
#when there are no differences in the entire sheet the dataframe will be empty. Add 1 to row indexes so the number coincides with excel rownumbers
if not df.empty:
# df.columns = ['A']
df.index = np.arange(1, len(df) + 1)
if not df2.empty:
# df2.columns = ['A']
df2.index = np.arange(1, len(df) + 1)
#delete rows with all 0
df = df.loc[~(df == 0).all(axis=1)]
df2 = df2.loc[~(df2 == 0).all(axis=1)]
#create new df with the data of the 2 sheets
result = pd.concat([df,df2],axis=1)
print(result)
result.to_excel(writer,sheet_name=x)
最新答案
方法
这是一个有趣的问题。另一种方法是使用Pandas提供的面板
数据结构,将一个Excel工作表中的列值与另一个Excel工作表中的列值进行比较。此数据结构将数据存储为三维数组。使用存储在面板中的两个Excel工作表中的数据,我们可以比较工作表中由一个或多个列(例如,唯一ID)唯一标识的行。要进行此比较,请应用自定义函数,将一个工作表中每列的每个单元格中的值与第二个工作表中同列的同一单元格中的值进行比较。这种方法的一个好处是,每个值的数据类型不再重要,因为我们只是比较值(例如,1==1
,“我的名字”==“我的名字”
,等等)
假设
这种方法对您的数据做出了以下几个假设:
每个工作表中的行共享唯一标识每一行的一列或一组列
两个工作表中都存在用于比较的列,它们共享相同的列标题
(我可能没有注意到其他假设。)
实施
这种方法的实现有点复杂。此外,由于我没有访问您的数据的权限,因此无法专门针对您的数据自定义实现。话虽如此,我将使用如下所示的一些虚拟数据来实现这种方法
“旧”数据集:
id col_num col_str col_datetime
1 123 My string 1 2001-12-04
2 234 My string 2 2001-12-05
3 345 My string 3 2001-12-06
id col_num col_str col_datetime
1 123 My string 1 MODIFIED 2001-12-04
3 789 My string 3 2001-12-10
4 456 My string 4 2001-12-07
“新”数据集:
id col_num col_str col_datetime
1 123 My string 1 2001-12-04
2 234 My string 2 2001-12-05
3 345 My string 3 2001-12-06
id col_num col_str col_datetime
1 123 My string 1 MODIFIED 2001-12-04
3 789 My string 3 2001-12-10
4 456 My string 4 2001-12-07
请注意这两个数据帧的以下差异:
df_old = pd.read_excel('old.xlsx', 'Sheet1', na_values=['NA'])
df_new = pd.read_excel('new.xlsx', 'Sheet1', na_values=['NA'])
df_old['VER'] = 'OLD'
df_new['VER'] = 'NEW'
col_num col_str col_datetime
id
1 123 My string 1 -> My string 1 MODIFIED 2001-12-04 00:00:00
3 345 -> 789 My string 3 2001-12-06 00:00:00 -> 2001-12-10 00:00:00
id为1的行中的col\u str
不同
id为3的行中的col_num
不同
id为3的行中的col\u datetime
不同
id为2的行存在于“旧”中,但不在“新”中
id为4的行存在于“新建”中,但不存在于“旧”中
好的,让我们开始吧。首先,我们将数据集读入单独的数据帧:
df_old = pd.read_excel('old.xlsx', 'Sheet1', na_values=['NA'])
df_new = pd.read_excel('new.xlsx', 'Sheet1', na_values=['NA'])
df_old['VER'] = 'OLD'
df_new['VER'] = 'NEW'
col_num col_str col_datetime
id
1 123 My string 1 -> My string 1 MODIFIED 2001-12-04 00:00:00
3 345 -> 789 My string 3 2001-12-06 00:00:00 -> 2001-12-10 00:00:00
然后,我们在每个数据帧中添加一个新的版本列,以保持思路的清晰。稍后,我们还将使用此列将“旧”和“新”数据帧中的行分离为各自独立的数据帧:
df_old = pd.read_excel('old.xlsx', 'Sheet1', na_values=['NA'])
df_new = pd.read_excel('new.xlsx', 'Sheet1', na_values=['NA'])
df_old['VER'] = 'OLD'
df_new['VER'] = 'NEW'
col_num col_str col_datetime
id
1 123 My string 1 -> My string 1 MODIFIED 2001-12-04 00:00:00
3 345 -> 789 My string 3 2001-12-06 00:00:00 -> 2001-12-10 00:00:00
然后,我们将“旧”和“新”数据集连接到一个数据帧中。请注意,ignore\u index
参数设置为True
,因此我们忽略索引,因为它对该操作没有意义:
df_full=pd.concat([df_old,df_new],ignore_index=True)
现在,我们将识别两个数据帧中存在的所有重复行。在这些行中,“旧”和“新”数据帧中的所有列值都相同。换句话说,这些是不存在差异的行:
一旦确定,我们将删除这些重复的行。我们剩下的是(a)两个数据帧之间不同的行,(b)存在于“旧”数据帧中但不存在于“新”数据帧中,以及(c)存在于“新”数据帧中但不存在于“旧”数据帧中的行:
df_diff=df_full。删除重复项(子集=['id','col_num','col_str','col_datetime'])
接下来,我们为存在于“旧”和“新”数据帧中的行识别并提取id
(即,跨越“旧”和“新”数据帧的主键)的值。需要注意的是,这些id
s不包括一个或其他数据帧中存在的行,但不包括两个数据帧中存在的行(即删除的行或添加的行):
diff\u id=df\u diff.set\u index('id').index.get\u duplicates()
现在,我们将df_full
限制为仅由diff_id
中的id
s标识的行:
df_diff_id=df_full[df_full['id'].isin(diff_id)]
现在,我们将“旧”和“新”数据帧中的重复行移动到单独的数据帧中,我们可以将这些数据帧插入面板
数据结构中进行比较:
df_diff_old = df_diff_ids[df_diff_ids['VER'] == 'OLD']
df_diff_new = df_diff_ids[df_diff_ids['VER'] == 'NEW']
接下来,我们将这两个数据帧的索引设置为主键(即,id
)。这是面板有效工作所必需的:
df_diff_old.set_index('id', inplace=True)
df_diff_new.set_index('id', inplace=True)
我们将这两个数据帧插入面板
数据结构:
df_panel=pd.panel(dict(df1=df_diff_old,df2=df_diff_new))
最后,我们使用自定义函数(find_diff
)和apply
方法进行比较:
def find_diff(x):
return x[0] if x[0] == x[1] else '{} -> {}'.format(*x)
df_diff = df_panel.apply(find_diff, axis=0)
如果打印出df_diff
的内容,您可以很容易地注意到在“旧”和“新”数据帧之间更改了哪些值:
df_old = pd.read_excel('old.xlsx', 'Sheet1', na_values=['NA'])
df_new = pd.read_excel('new.xlsx', 'Sheet1', na_values=['NA'])
df_old['VER'] = 'OLD'
df_new['VER'] = 'NEW'
col_num col_str col_datetime
id
1 123 My string 1 -> My string 1 MODIFIED 2001-12-04 00:00:00
3 345 -> 789 My string 3 2001-12-06 00:00:00 -> 2001-12-10 00:00:00
改进
对于这个实现,我将留给您做一些改进
添加一个二进制(1/0)标志,指示
换行
确定删除了“旧”数据框中的哪些行
(即,“新”数据框中不存在)
确定列表中的哪些行
添加了“新”数据帧(即,“旧”数据帧中不存在)
原始答案
问题:
问题是您无法对datetimes
执行算术运算
但是,您可以对时间增量执行算术运算。