Pandas出类拔萃

2024-06-28 15:40:26 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试创建一个python程序,它提供了两个具有多个工作表的大excel文件之间的差异。我让它把结果打印到excel中,但显然当其中一个单元格包含日期时间数据时,将布尔数据帧与包含日期的数据帧相乘的操作不再有效。我得到以下错误:

TypeError:不支持*:“bool”和“的操作数类型”日期时间。日期时间'

“EDIT”:我刚刚意识到这个方法也不适用于字符串(它只适用于纯数值数据)。对于字符串、数字和时间数据,有什么更好的方法呢?在

#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)

Tags: the数据importnonedfreadinputwith
1条回答
网友
1楼 · 发布于 2024-06-28 15:40:26

更新的答案

方法

这是一个有趣的问题。另一种方法是使用Pandas提供的Panel数据结构,将一个Excel工作表中的列值与另一个Excel工作表中的列值进行比较。此数据结构将数据存储为三维数组。通过存储在Panel中的两个Excel工作表中的数据,我们可以比较由一个或多个列(例如,一个唯一的ID)唯一标识的工作表中的行。通过应用自定义函数将一个工作表中每个列的每个单元格中的值与第二个工作表中同一列的同一单元格中的值进行比较。这种方法的一个好处是每个值的数据类型不再重要,因为我们只是比较值(例如,1 == 1'my name' == 'my name',等等)。在

假设

这种方法对您的数据进行了若干假设:

  1. 每个工作表中的行共享一个或多个列,这些列唯一地标识每一行。在
  2. 两个工作表中都存在要比较的列,它们共享相同的列标题。在

(我可能没有注意到其他假设。)

实施

这种方法的实现有点复杂。另外,因为我没有访问您的数据的权限,所以我无法针对您的数据定制具体的实现。说到这里,我将使用下面显示的一些虚拟数据来实现这个方法。在

“旧”数据集:

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

“新”数据集:

^{pr2}$

请注意这两个数据帧的以下差异:

  1. col_str与{}1的行不同
  2. col_numid3的行不同
  3. col_datetimeid3的行不同
  4. id2的行存在于“old”中,而不是“new”中
  5. id4的行存在于“new”中,而不是“old”中

好吧,我们开始吧。在第一个数据帧中,我们将读取数据集:

^{3}$

然后我们在每个数据帧中添加一个新的版本列,以保持我们的思路清晰。我们稍后还将使用此列将“旧”和“新”数据帧中的行分离到各自独立的数据帧中:

df_old['VER'] = 'OLD'
df_new['VER'] = 'NEW'

然后我们将“旧”和“新”数据集连接到单个数据帧中。请注意,ignore_index参数设置为True,因此我们忽略了索引,因为它对该操作没有意义:

df_full = pd.concat([df_old, df_new], ignore_index=True)

现在我们要标识两个数据帧之间存在的所有重复行。这些行中的所有列值在“旧”和“新”数据帧中都相同。换句话说,这些行不存在差异:

一旦确定,我们就删除这些重复的行。我们剩下的是(a)两个数据帧之间不同的行,(b)存在于“旧”数据帧中而不是“新”数据帧中,(c)存在于“新”数据帧中而不是“旧”数据帧中:

df_diff = df_full.drop_duplicates(subset=['id', 'col_num', 'col_str', 'col_datetime'])

接下来,我们识别并提取id(即“旧”和“新”数据帧之间的主键)的值,这些值分别存在于“旧”和“新”数据帧中。需要注意的是,这些ids包含存在于一个或其他数据帧中的行,但不包括这两个数据帧中的行(即,删除的行或添加的行):

diff_ids = df_diff.set_index('id').index.get_duplicates()

现在我们将df_full仅限于diff_ids中由id标识的行:

df_diff_ids = df_full[df_full['id'].isin(diff_ids)]

现在,我们将“旧”和“新”数据帧中的重复行移动到单独的数据帧中,我们可以插入Panel数据结构进行比较:

df_diff_old = df_diff_ids[df_diff_ids['VER'] == 'OLD']
df_diff_new = df_diff_ids[df_diff_ids['VER'] == 'NEW']

接下来,我们将这两个数据帧的索引都设置为主键(即id)。这是Panel有效工作所必需的:

df_diff_old.set_index('id', inplace=True)
df_diff_new.set_index('id', inplace=True)

我们把这两个都开槽将数据帧放入Panel数据结构中:

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的内容,您可以很容易地注意到在“旧”和“新”数据帧之间发生了哪些值的更改:

^{8}$

改进

我会给你留下一些改进的地方。在

  1. 添加一个二进制(1/0)标志,该标志指示 行已更改
  2. 标识“旧”数据帧中已删除的行 (即“新”数据帧中不存在)
  3. 标识 添加了“新”数据帧(即“旧”数据帧中不存在)

原始答案

问题:

问题是您不能对datetimes执行算术运算。在

但是,您可以对timedeltas执行算术运算。在

我可以想出一些可能对您有帮助的解决方案:

解决方案1:

将您的datetimes转换为字符串。

如果我正确地理解了你的问题,你在比较Excel工作表中的差异,对吗?如果是这样,那么我认为datetimes是否表示为显式datetimes(即,您没有执行任何datetime计算)也不重要。在

要实现此解决方案,您需要将pd.read_excel()' calls and explicitly set the数据类型parameter to convert your日期时间修改为字符串:

df1 = pd.read_excel('4_Input EfE_2030.xlsm', dtypes={'LABEL FOR DATETIME COL 1': str})

解决方案2:

将您的datetimes转换为timedeltas

对于每个datetime列,可以使用:pd.Timedelta(df['LABEL FOR DATETIME COL'])

总的来说,在没有看到您的数据的情况下,我认为解决方案1是最直接的。在

相关问题 更多 >