Python在追加到空数组时更改pandas DF数据



在工作时,我遇到了一些关于Pandas DF和append的奇怪行为。

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

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

这是在Python 3.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

使用此代码,您可以看到有两种存储数据的方法:

  1. 使用BuggedResult.append()
  2. 采用Pandas的pd.append()方法

BuggedResult(BuggedResult[0](数组的第一个元素正常,看起来像这样:

┌──────────────┬────────────┐
│ FillIntStart │ FillIntEnd │
├──────────────┼────────────┤
│            2 │          3 │
│            2 │          4 │
│            2 │          5 │
│            2 │          6 │
│            2 │          7 │
│            2 │          8 │
│            2 │          9 │
│            2 │         10 │
└──────────────┴────────────┘

但第二个元素(BuggedResult[1](看起来是这样的:

┌──────────────┬────────────┐
│ FillIntStart │ FillIntEnd │
├──────────────┼────────────┤
│            3 │          4 │
│            4 │          5 │
│            5 │          6 │
│            6 │          7 │
│            6 │          8 │
│            6 │          9 │
│            6 │         10 │
└──────────────┴────────────┘

当它看起来像这样时(取自CorrecResult表,使用pd.append()(:

┌──────────────┬────────────┐
│ FillIntStart │ FillIntEnd │
├──────────────┼────────────┤
│            3 │          4 │
│            3 │          5 │
│            3 │          6 │
│            3 │          7 │
│            3 │          8 │
│            3 │          9 │
│            3 │         10 │
└──────────────┴────────────┘

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

这是Python 3.7.4的一些奇怪的预期行为吗?可能不建议在空数组中添加DF,因为Panda已经有了解决方案,但我认为更改数据太多了。

我真诚地希望这个问题是我的,因为我不是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是导致错误的原因。当我在pdb中逐步查看您的代码(调试自己的代码是一项值得学习的技能(时,我发现这里发生了什么:由于您正在原地修改DataFrame,您随后的循环最终会修改已经在BuggedResult列表中的同一个DataFrame实例。DataFrame.append不会出现这个问题,因为它将数据复制到CorrectResult中,并在此过程中调整其数据缓冲区的大小。

如果可能的话,我会尽量避免使用DataFrame.append——在这里,像我的例子中那样使用单个pd.concat更有效,因为它可以为所有输出构建一个正确大小的单个DataFrame,然后复制到其中一次,而不是调整每个循环上的数据大小。也许有一个更好的解决方案,但目前还没有想到。

(顺便说一句,Python有一个关于如何格式化代码的风格指南PEP 8,它建议变量名使用小写,而CamelCase通常是为类名保留的。当然,没有要求你使用它,一致性最重要。但大多数Python社区都试图遵守这些约定,所以阅读不这样做的代码有点不舒服(。

最新更新