Python在附加到空数组时更改数据

Python在附加到空数组时更改数据,python,arrays,pandas,Python,Arrays,Pandas,在工作中,我遇到了一些关于熊猫DF和append的奇怪行为 目标是使用RowNum行数生成DF,重复UniqueInt值,并从UniqueInt+1开始直到RowNum生成一列新的连续数字。这是一种用相同的先前数据填充中间的空数据的方法 我主要关心的不是如何实现这一点,而是为什么在将结果追加到空数据帧时,结果数据与代码的输出不一致(即:追加会更改所追加数据的值) 这是在Python3.7.4上实现的 我创建了一个非常小的可复制示例: import pandas as pd import nump

在工作中,我遇到了一些关于熊猫DF和append的奇怪行为

目标是使用
RowNum
行数生成DF,重复
UniqueInt
值,并从
UniqueInt
+1开始直到
RowNum
生成一列新的连续数字。这是一种用相同的先前数据填充中间的空数据的方法

我主要关心的不是如何实现这一点,而是为什么在将结果追加到空数据帧时,结果数据与代码的输出不一致(即:追加会更改所追加数据的值)

这是在Python3.7.4上实现的

我创建了一个非常小的可复制示例:

import pandas as pd
import numpy as np    

#Create a DF
TemporalDF=pd.DataFrame([2,2,3,3,3,7,7,7,8,8,8,9,9,10,10,10])
TemporalDF.columns=['Int']

#Create recipients for data
BuggedResult=[]
CorrectResult=pd.DataFrame()

# For loop
for UniqueInt in range(TemporalDF['Int'].unique()[0],10):
    # Specify desired number of rows
    RowNum=(10-UniqueInt)

    # Subset original data
    Temp=TemporalDF[TemporalDF['Int']==UniqueInt]

    # Fill gaps of data based on last correctly recorded data
    if(Temp.shape[0]==0):
        # Take last recorded value
        DummyDF=DummyDF.iloc[1:DummyDF.shape[0]+1,:]
        DummyDF['FillIntStart']=np.repeat(a=UniqueInt, repeats=RowNum)
    else:         
        # Create empty data frame 
        DummyDF=pd.DataFrame()

        # Populate
        DummyDF['FillIntStart']=np.repeat(a=UniqueInt, repeats=RowNum)
        DummyDF['FillIntEnd']=[UniqueInt+i for i in range(1,RowNum+1)]

    # Save results
    BuggedResult.append(DummyDF)
    CorrectResult=CorrectResult.append(other=DummyDF, ignore_index=True)
pass
使用此代码,您可以看到有两种存储数据的方法:

  • 使用
    BuggedResult.append()
  • 使用Pandas的
    pd.append()
    方法
  • BuggedResult
    BuggedResult[0]
    )数组的第一个元素正常,如下所示:

    ┌──────────────┬────────────┐
    │ FillIntStart │ FillIntEnd │
    ├──────────────┼────────────┤
    │            2 │          3 │
    │            2 │          4 │
    │            2 │          5 │
    │            2 │          6 │
    │            2 │          7 │
    │            2 │          8 │
    │            2 │          9 │
    │            2 │         10 │
    └──────────────┴────────────┘
    
    ┌──────────────┬────────────┐
    │ FillIntStart │ FillIntEnd │
    ├──────────────┼────────────┤
    │            3 │          4 │
    │            4 │          5 │
    │            5 │          6 │
    │            6 │          7 │
    │            6 │          8 │
    │            6 │          9 │
    │            6 │         10 │
    └──────────────┴────────────┘
    
    但是第二个元素(
    BuggedResult[1]
    )如下所示:

    ┌──────────────┬────────────┐
    │ FillIntStart │ FillIntEnd │
    ├──────────────┼────────────┤
    │            2 │          3 │
    │            2 │          4 │
    │            2 │          5 │
    │            2 │          6 │
    │            2 │          7 │
    │            2 │          8 │
    │            2 │          9 │
    │            2 │         10 │
    └──────────────┴────────────┘
    
    ┌──────────────┬────────────┐
    │ FillIntStart │ FillIntEnd │
    ├──────────────┼────────────┤
    │            3 │          4 │
    │            4 │          5 │
    │            5 │          6 │
    │            6 │          7 │
    │            6 │          8 │
    │            6 │          9 │
    │            6 │         10 │
    └──────────────┴────────────┘
    
    当它看起来像这样时(取自CorrecResult表,使用
    pd.append()
    ):

    换句话说,append方法是在我追加数据后更改我的数据。如果您检查代码,还可以尝试我已经尝试过的几种方法,比如手动跟踪循环,添加
    DummyDF.to_txt()
    方法来读取单独文件中的数据,等等。逻辑似乎还可以,但当我将其附加到空数组时,结果会发生变化

    这是Python3.7.4所期望的奇怪行为吗?不建议在空数组中添加DF,因为pandas已经有了解决方案,但我认为更改数据太多了

    我真诚地希望这个问题是我的,因为我不是Python专家。。。那么,有什么想法吗

    谢谢

    我是这样做的:

    >>> temporal = np.array([2,2,3,3,3,7,7,7,8,8,8,9,9,10,10,10])
    >>> max_temporal = np.max(temporal)
    >>> result = []
    >>> columns = ['FillIntStart', 'FillIntEnd']
    >>> for x in np.unique(temporal):
    ...     start = np.repeat(x, max_temporal - x)
    ...     end = np.arange(x + 1, max_temporal + 1)
    ...     result.append(pd.DataFrame({columns[0]: start, columns[1]: end}, columns=columns))
    ...     
    >>> result = pd.concat(result)
    >>> print(result.to_string(index=False))
    FillIntStart  FillIntEnd
               2           3
               2           4
               2           5
               2           6
               2           7
               2           8
               2           9
               2          10
               3           4
               3           5
               3           6
               3           7
               3           8
               3           9
               3          10
               7           8
               7           9
               7          10
               8           9
               8          10
               9          10
    
    如果我理解的话,这就是你想要达到的结果

    我必须仔细查看您的代码,以了解它的错误。特别是,我不太明白这部分到底在做什么:

        # Fill gaps of data based on last correctly recorded data
        if(Temp.shape[0]==0):
            # Take last recorded value
            DummyDF=DummyDF.iloc[1:DummyDF.shape[0]+1,:]
            DummyDF['FillIntStart']=np.repeat(a=UniqueInt, repeats=RowNum)
    
    此代码一开始就有问题,因为运行时可能尚未定义
    DummyDF
    (仅当
    else:
    块在上一个循环中运行时)。我不太清楚在这种情况下你想做什么,因为它似乎在处理[2,10]范围内的缺失值,而这些值不在你原来的
    TemporalDF
    中,我认为你没有解释在这种情况下你想做什么。您正在重用前面循环中的
    DummyDF
    这一事实是导致您的bug的原因。当我逐步了解您的代码时(这是一项值得学习的调试您自己的代码的技能),我发现这里发生了什么:因为您正在修改一个
    DataFrame
    ,您随后的循环最终修改了
    BuggedResult
    列表中已经存在的同一个
    DataFrame
    实例。使用
    DataFrame.append
    不会出现此问题,因为它会将数据复制到
    CorrectResult
    ,并在此过程中调整其数据缓冲区的大小

    如果可能的话,我会尽量避免使用
    DataFrame.append
    ——这里使用一个
    pd.concat
    ,就像我的示例中那样,效率更高,因为它可以为所有输出构建一个大小正确的
    DataFrame
    ,然后复制一次。也许有更好的解决办法,但目前还没有想到


    (顺便说一句,Python有一个关于如何格式化代码的样式指南,建议变量名使用小写,而CamelCase通常用于类名。当然,不要求您使用小写,一致性是最重要的。但大多数Python社区都试图坚持这些约定,因此有点不一致ng以读取不需要的代码。

    感谢您的回答。是的,代码正在尝试处理中间缺少的值,用来自上一个DummyDF的相同数据填充它们。因为我使用的数据的性质是不可能的,因为在计算Temp.shape[0]==0时,第一个循环返回True,所以这永远不会是一个问题(当然,在我的特定场景中)。我知道还有其他方法可以解决这个问题,但我关心的不是如何解决,而是为什么将数据添加到空数组会更改数据。另外,非常感谢样式指南!我刚刚更新了我的答案,试图解释这个错误。我不太清楚您想要如何处理这个错误“缺少”值:如果您的数据范围从[2,10]开始,为什么还要费心调用
    .unique()
    呢?因为我使用的数据的性质是不可能的,因为在计算Temp.shape[0]==0时,第一个循环返回True“在这种特殊情况下可能是这样,但正如您自己所指出的,它不是非常健壮,而且会使代码更加混乱和难以理解。我将避免将此作为一般原则。
    append
    返回一个新的数据帧,并且不会修改原始数据帧。