创建一个空的 Pandas DataFrame,然后填充它

2024-12-13 08:36:00
admin
原创
128
摘要:问题描述:我从这里的 pandas DataFrame 文档开始:数据结构简介我想在时间序列类型的计算中迭代地用值填充 DataFrame。我想用列 A、B 和时间戳行初始化 DataFrame,全部为 0 或全部为 NaN。然后,我会添加初始值,并检查该数据,根据前一行计算出新行,等等row[A][t] =...

问题描述:

我从这里的 pandas DataFrame 文档开始:数据结构简介

我想在时间序列类型的计算中迭代地用值填充 DataFrame。我想用列 A、B 和时间戳行初始化 DataFrame,全部为 0 或全部为 NaN。

然后,我会添加初始值,并检查该数据,根据前一行计算出新行,等等row[A][t] = row[A][t-1]+1

我目前正在使用如下代码,但我觉得它有点丑陋,并且必须有一种方法可以直接使用 DataFrame 来做到这一点,或者总体上只是一种更好的方法。

import pandas as pd
import datetime as dt
import scipy as s
base = dt.datetime.today().date()
dates = [ base - dt.timedelta(days=x) for x in range(9, -1, -1) ]

valdict = {}
symbols = ['A','B', 'C']
for symb in symbols:
    valdict[symb] = pd.Series( s.zeros(len(dates)), dates )

for thedate in dates:
    if thedate > dates[0]:
        for symb in valdict:
            valdict[symb][thedate] = 1 + valdict[symb][thedate - dt.timedelta(days=1)]

解决方案 1:

永远不要逐行增加 DataFrame !

TLDR:(只需阅读粗体文字)

这里的大多数答案都会告诉您如何创建一个空的 DataFrame 并填充它,但没有人会告诉您这是一件坏事。

这是我的建议:在列表中累积数据,而不是在 DataFrame 中。

使用列表收集数据,然后在准备就绪时初始化 DataFrame。列表列表字典列表格式均可,pd.DataFrame两种格式均可。

data = []
for row in some_function_that_yields_data():
    data.append(row)

df = pd.DataFrame(data)

pd.DataFrame将行列表(其中每行都是标量值)转换为 DataFrame。如果您的函数返回DataFrame的是 s,请调用pd.concat

这种方法的优点:

  1. 一次性附加到列表并创建 DataFrame 总是比创建一个空的 DataFrame(或 NaN 之一)并一遍又一遍地附加到其中更便宜。

  2. 列表还占用更少的内存,并且是一种更轻量的数据结构,可用于处理、附加和删除(如果需要)。

  3. dtypes都是自动推断的(而不是分配object给所有)。

  4. RangeIndex系统会自动为您的数据创建A,而您无需在每次迭代时小心地为要附加的行分配正确的索引。

如果你还不相信的话,文档中也提到了这一点:

迭代地将行附加到 DataFrame 比单次连接需要更多的计算资源。更好的解决方案是将这些行附加到列表中,然后将列表与原始 DataFrame 一次性连接起来。

pandas >= 2.0 更新:append已被删除!

DataFrame.append在版本 1.4 中已弃用,并在版本 2.0 中完全从 pandas API 中删除。另请参阅最初提议弃用的github 问题。



这些选择很糟糕

append或者concat在循环内

以下是我发现的初学者最大的错误:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df = df.append({'A': i, 'B': b, 'C': c}, ignore_index=True) # yuck
    # or similarly,
    # df = pd.concat([df, pd.Series({'A': i, 'B': b, 'C': c})], ignore_index=True)

append每次执行或操作时都会重新分配内存concat。将其与循环结合起来,您就会得到一个二次复杂度的运算

与之相关的另一个错误df.append是,用户往往会忘记append 不是一个就地函数,因此必须将结果分配回去。您还需要担心 dtypes:

df = pd.DataFrame(columns=['A', 'B', 'C'])
df = df.append({'A': 1, 'B': 12.3, 'C': 'xyz'}, ignore_index=True)

df.dtypes
A     object   # yuck!
B    float64
C     object
dtype: object

处理对象列从来都不是一件好事,因为 pandas 无法对这些列上的操作进行矢量化。你需要调用该infer_objects()方法来修复它:

df.infer_objects().dtypes
A      int64
B    float64
C     object
dtype: object

loc在循环内

我还看到过loc用于附加到创建为空的 DataFrame 的方法:

df = pd.DataFrame(columns=['A', 'B', 'C'])
for a, b, c in some_function_that_yields_data():
    df.loc[len(df)] = [a, b, c]

和以前一样,您没有预先分配每次所需的内存量,因此每次创建新行时都会重新增加内存。它和一样糟糕append,甚至更丑陋。

空的 DataFrame 包含 NaN

然后,创建一个 NaN 的 DataFrame,以及与之相关的所有注意事项。

df = pd.DataFrame(columns=['A', 'B', 'C'], index=range(5))
df
     A    B    C
0  NaN  NaN  NaN
1  NaN  NaN  NaN
2  NaN  NaN  NaN
3  NaN  NaN  NaN
4  NaN  NaN  NaN

object它像其他的一样创建一个由列组成的 DataFrame 。

df.dtypes
A    object  # you DON'T want this
B    object
C    object
dtype: object

附加方法仍然存在上述所有问题。

for i, (a, b, c) in enumerate(some_function_that_yields_data()):
    df.iloc[i] = [a, b, c]


实践才是检验的依据

对这些方法进行计时是了解它们在内存和实用性方面差异的最快方法。

绘制最多 1000 行的数据框,显示 list.append 速度提高了 2-3 个数量级

供参考的基准代码。

解决方案 2:

以下是一些建议:

用于date_range索引:

import datetime
import pandas as pd
import numpy as np

todays_date = datetime.datetime.now().date()
index = pd.date_range(todays_date-datetime.timedelta(10), periods=10, freq='D')

columns = ['A','B', 'C']

注意:我们可以通过简单的编写来创建一个空的 DataFrame(带有NaNs):

df_ = pd.DataFrame(index=index, columns=columns)
df_ = df_.fillna(0) # With 0s rather than NaNs

要对数据执行这些类型的计算,请使用NumPy数组:

data = np.array([np.arange(10)]*3).T

因此我们可以创建 DataFrame:

In [10]: df = pd.DataFrame(data, index=index, columns=columns)

In [11]: df
Out[11]:
            A  B  C
2012-11-29  0  0  0
2012-11-30  1  1  1
2012-12-01  2  2  2
2012-12-02  3  3  3
2012-12-03  4  4  4
2012-12-04  5  5  5
2012-12-05  6  6  6
2012-12-06  7  7  7
2012-12-07  8  8  8
2012-12-08  9  9  9

解决方案 3:

如果您只是想创建一个空数据框并稍后用一些传入数据框填充它,请尝试以下操作:

newDF = pd.DataFrame() #creates a new dataframe that's empty
newDF = newDF.append(oldDF, ignore_index = True) # ignoring index is optional
# try printing some data from newDF
print newDF.head() #again optional 

在这个例子中,我使用这个 pandas 文档来创建一个新的数据框,然后使用append将来自 oldDF 的数据写入 newDF。

如果我必须继续将来自多个 oldDF 的新数据附加到这个 newDF 中,我只需使用 for 循环遍历
pandas.DataFrame.append()

注意:append()自 1.4.0 版本起已弃用。使用concat()

解决方案 4:

使用列名初始化空框架

import pandas as pd

col_names =  ['A', 'B', 'C']
my_df  = pd.DataFrame(columns = col_names)
my_df

向框架添加新记录

my_df.loc[len(my_df)] = [2, 4, 5]

您可能还想传递一本字典:

my_dic = {'A':2, 'B':4, 'C':5}
my_df.loc[len(my_df)] = my_dic 

将另一个框架附加到现有框架

col_names =  ['A', 'B', 'C']
my_df2  = pd.DataFrame(columns = col_names)
my_df = my_df.append(my_df2)

性能注意事项

如果在循环中添加行,请考虑性能问题。对于前 1000 条记录,“my_df.loc” 的性能较好,但随着循环中记录数量的增加,性能会逐渐变慢。

如果您打算在一个大循环中执行细化操作(例如 10M‌ 条记录左右),最好将这两种方法混合使用;使用 iloc 填充数据框,直到大小达到 1000 左右,然后将其附加到原始数据框,并清空临时数据框。这将使您的性能提高约 10 倍。

解决方案 5:

简单地:

import numpy as np
import pandas as pd

df=pd.DataFrame(np.zeros([rows,columns])

然后填充。

解决方案 6:

假设一个数据框有 19 行

index=range(0,19)
index

columns=['A']
test = pd.DataFrame(index=index, columns=columns)

将 A 列保持为常量

test['A']=10

将 b 列保留为循环给出的变量

for x in range(0,19):
    test.loc[[x], 'b'] = pd.Series([x], index = [x])

pd.Series([x], index = [x])你可以用任意值替换第一个 x

解决方案 7:

这是我使用循环从多个列表创建动态数据框的方法

x = [1,2,3,4,5,6,7,8]
y = [22,12,34,22,65,24,12,11]
z = ['as','ss','wa', 'ss','er','fd','ga','mf']
names = ['Bob', 'Liz', 'chop']

循环

def dataF(x,y,z,names):
    res = []

    for t in zip(x,y,z):
        res.append(t)

    return pd.DataFrame(res,columns=names)

结果

dataF(x,y,z,names)

在此处输入图片描述

解决方案 8:

Pandas 数据框可以看作是 Pandas 列的字典(Pandas 系列)。就像字典中添加新的键值对的成本很低一样,添加新的列/列也非常高效(数据框就是这样水平增长的)。

df = pd.DataFrame()
df['A'] = range(0, 2000_000, 2)   # add one column
df[['B', 'C']] = ['a', 'b']       # add multiple columns

另一方面,就像更新字典中的每个值都需要循环遍历整个字典一样,通过添加新行来垂直扩大数据框的效率非常低。如果在循环中逐行添加新行,则效率尤其低下(请参阅此帖子,了解比较可能选项的基准)。

如果新行值依赖于 OP 中的前一行值,那么根据列数,最好循环遍历预先初始化的零数据框,或者在循环中增加 Python 字典,然后构造数据框(如果有超过 500 列,最好循环遍历数据框)。但将两者混合使用永远不是最佳选择,换句话说,增加 pandas Series 字典将非常慢。1

dates = pd.date_range(end=pd.Timestamp('now'), periods=10000, freq='D').date
symbols = [f"col{i}" for i in range(10)]

# initialize a dataframe
df = pd.DataFrame(0, index=dates, columns=symbols)
# update it in a loop
for i, thedate in enumerate(df.index):
    if thedate > df.index[0]:
        df.loc[thedate] = df.loc[df.index[i-1]] + 1


# build a nested dictionary
data = {}
for i, thedate in enumerate(dates):
    for symb in symbols:
        if thedate > dates[0]:
            data[symb][thedate] = 1 + data[symb][dates[i-1]]
        else:
            data[symb] = {thedate: 0}
# construct a dataframe after
df1 = pd.DataFrame(data)

1:也就是说,对于这个特定的例子,cumsum()甚至range()似乎不需要循环遍历行就可以工作。答案的这一部分更多地是关于循环不可避免的情况,例如财务数据操作等。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用