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

Posted

技术标签:

【中文标题】创建一个空的 Pandas DataFrame,然后填充它?【英文标题】:Creating an empty Pandas DataFrame, then filling it? 【发布时间】:2012-11-26 21:03:07 【问题描述】:

我从 pandas DataFrame 文档开始:http://pandas.pydata.org/pandas-docs/stable/dsintro.html

我想用时间序列计算中的值迭代地填充 DataFrame。 所以基本上,我想用列 A、B 和时间戳行来初始化 DataFrame,全部为 0 或全部为 NaN。

然后我会添加初始值并检查这些数据,从前一行计算新行,比如row[A][t] = row[A][t-1]+1 左右。

我目前正在使用下面的代码,但我觉得它有点难看,必须有一种方法可以直接使用 DataFrame 来做到这一点,或者一般来说只是一种更好的方法。 注意:我使用的是 Python 2.7。

import datetime as dt
import pandas as pd
import scipy as s

if __name__ == '__main__':
    base = dt.datetime.today().date()
    dates = [ base - dt.timedelta(days=x) for x in range(0,10) ]
    dates.sort()

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

    print valdict

【问题讨论】:

永远不要增长 DataFrame!在内存和性能方面,附加到 python 列表然后将其转换为 DataFrame 总是更便宜。 @cs95 pd 中的.append 和附加列表在功能上有何不同?我知道.appendin pandas 将整个数据集复制到一个新对象´,python 的附加工作方式是否不同? @Lamma 请在下面我的回答中找到详细信息。附加到 df 时,每次都会在内存中创建一个新的 DataFrame 而不是使用现有的,坦率地说这是一种浪费。 【参考方案1】:

简单地说:

import numpy as np
import pandas as pd

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

然后填充它。

【讨论】:

【参考方案2】:

这是我用循环从多个列表中制作动态数据框的方法

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)

【讨论】:

【参考方案3】:

永远不要增长 DataFrame!

TLDR; (只需阅读粗体字)

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

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

使用列表收集数据,然后在准备好时初始化 DataFrame。 list-of-lists 或 list-of-dicts 格式都可以使用,pd.DataFrame 两者都接受。

data = []
for a, b, c in some_function_that_yields_data():
    data.append([a, b, c])

df = pd.DataFrame(data, columns=['A', 'B', 'C'])

这种方法的优点:

    与创建一个空的 DataFrame(或 NaN 之一)并一遍又一遍地附加到它相比,添加到列表并一次性创建一个 DataFrame 总是更便宜。

    列表占用的内存也更少,并且是一种更轻便的数据结构,可以使用、追加和删除(如果需要)。

    dtypes 是自动推断出来的(而不是将object 分配给所有这些)。

    会自动为您的数据创建RangeIndex,您不必小心为每次迭代时附加的行分配正确的索引。

如果你还不服气,documentation中也提到了这一点:

迭代地将行附加到 DataFrame 可以在计算上更加高效 比单个连接更密集。更好的解决方案是追加 这些行到一个列表,然后将列表与原始列表连接起来 一次全部使用 DataFrame。

但是,如果我的函数返回需要合并成一个大数据帧的较小数据帧怎么办?

没关系,您仍然可以通过增长或创建较小 DataFrames 的 python 列表,然后调用 pd.concat,在线性时间内完成此操作。

small_dfs = []
for small_df in some_function_that_yields_dataframes():
    small_dfs.append(small_df)

large_df = pd.concat(small_dfs, ignore_index=True)

或者,更简洁:

large_df = pd.concat(
    list(some_function_that_yields_dataframes()), ignore_index=True)


这些选项太可怕了

appendconcat 在循环内

这是我从初学者那里看到的最大错误:

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)

内存会为您的每个appendconcat 操作重新分配。将此与循环结合起来,您就有了二次复杂度运算

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 无法对这些列进行矢量化操作。你需要这样做来修复它:

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一样糟糕,甚至更丑。

NaN 的空 DataFrame

然后,创建一个包含 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

它创建一个对象列的 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]


证据就在布丁里

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

Benchmarking code for reference.

【讨论】:

这确实在文档中。 “迭代地将行附加到 DataFrame 可能比单个连接在计算上更密集。更好的解决方案是将这些行附加到列表中,然后一次将列表与原始 DataFrame 连接起来。” pandas.pydata.org/pandas-docs/version/0.21/generated/… Also "注意值得注意的是 concat() (因此 append() )会制作数据的完整副本,并且不断重用此函数会显着降低性能。如果您需要要对多个数据集使用该操作,请使用列表推导。” pandas.pydata.org/pandas-docs/stable/user_guide/… 那么,当我的数据以 1d 的形式“进来”时,我该怎么办?一次列出一个,每个代表数据框中的一列?在转换为数据框之前如何将它们附加在一起?似乎list1.apped(list2) 在另一个列表中插入了一个列表,而不是添加一列。谢谢 @Confounded 这与这里提出的问题不同,但一次将一列分配给空数据框应该没问题。连续添加行会出现问题。 @micstr a、b 和 c 是单独的原子标量值,而不是列表。如果您已经有可用的数据列表,只需致电pd.DataFrame([a_list, b_list, c_list])【参考方案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 倍。

【讨论】:

my_df = my_df.append(my_df2) 对我不起作用,除非我指定 ignore_index=True【参考方案5】:

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

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 

在本例中,我使用this pandas doc 创建一个新数据框,然后使用append 将oldDF 中的数据写入newDF。

如果我必须继续将新数据附加到这个 newDF 中 一个 oldDF,我只是使用一个 for 循环来迭代 pandas.DataFrame.append()

【讨论】:

请注意,append(以及类似的concat)每次都会将完整的数据集复制到一个新对象,因此,迭代和追加可能并且将导致重大的性能损失。更多信息请参考:pandas.pydata.org/pandas-docs/stable/merging.html @MoustafaAAtta 将迭代数据附加到数据帧的替代方法是什么? @MoustafaAAtta Fred 在这篇文章中的回答是:***.com/questions/10715965/… 在这个观点上更好吗? @MoustafaAAtta 您也许可以只将行附加到数据框中,它仍会创建一个新对象,但对于较小的数据集,可能会有用。 pandas.pydata.org/pandas-docs/stable/user_guide/…【参考方案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】:

这里有几个建议:

使用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

【讨论】:

pd.date_range() 对我不起作用。我尝试使用 DateRange(来自 eclipse 的自动完成功能),但它可以将字符串用作日期格式,对吧?总体方法虽然有效(我将索引更改为其他内容)。 date_range 是一个用于创建日期时间索引的工厂函数,并且是 a new feature in 0.8.0,我肯定会建议升级到最新的稳定版本(0.9.1),其中包含许多错误修复和新功能。 :) 我注意到导入语句中的示例中有一个错字。它说:import datatime 它应该说:import datetime 这可能是你遇到困难的原因。 根据我的经验,创建一个用 NaN 填充的必要大小的数据框,然后用值填充比创建具有 index x 0 尺寸的数据框要慢得多( columns = []),并在循环的每一圈中附加一列。我的意思是df[col_name] = pandas.Series([...]) 在循环中遍历列名。在前一种情况下,不仅内存分配需要时间,而且用新值替换 NaN 似乎非常慢。 @deenes 绝对是。这个答案可能应该更清楚 - 你很少(如果有的话)想要创建一个空的数据框(NaN)。

以上是关于创建一个空的 Pandas DataFrame,然后填充它?的主要内容,如果未能解决你的问题,请参考以下文章

将dict动态附加到空的Pandas.Dataframe中[重复]

Python Pandas:获取列不为空的DataFrame的行

遇到的问题---python---pandas新建空的dataframe报错dataframe type object ‘object‘ has no attribute ‘dtype‘

遇到的问题---python---pandas新建空的dataframe报错dataframe type object ‘object‘ has no attribute ‘dtype‘(

最全的pandas面试基础100题目

在 np.where 条件下使用 pandas 可为空的整数 dtype