【问题标题】:Create a pandas DataFrame from generator?从生成器创建一个熊猫数据框?
【发布时间】:2013-09-25 19:01:47
【问题描述】:

我创建了一个元组生成器,它从文件中提取信息,仅过滤感兴趣的记录并将其转换为生成器返回的元组。

我尝试从以下位置创建一个 DataFrame:

import pandas as pd
df = pd.DataFrame.from_records(tuple_generator, columns = tuple_fields_name_list)

但会抛出错误:

... 
C:\Anaconda\envs\py33\lib\site-packages\pandas\core\frame.py in from_records(cls, data, index, exclude, columns, coerce_float, nrows)
   1046                 values.append(row)
   1047                 i += 1
-> 1048                 if i >= nrows:
   1049                     break
   1050 

TypeError: unorderable types: int() >= NoneType()

我设法让它在列表中消耗生成器,但使用两倍内存:

df = pd.DataFrame.from_records(list(tuple_generator), columns = tuple_fields_name_list)

我要加载的文件很大,而且内存消耗很重要。最后一次尝试我的电脑花了两个小时试图增加虚拟内存:(

问题:有谁知道直接从记录生成器创建数据帧的方法,而无需事先将其转换为列表?

注意:我在 Windows 上将 python 3.3 和 pandas 0.12 与 Anaconda 一起使用。

更新:

读取文件不是问题,我的元组生成器做得很好,它逐行扫描混合记录的文本压缩文件,只将想要的数据转换为正确的类型,然后在元组生成器中生成字段形式。 有些数字,它在大约一分钟内扫描了 130MB gzip 文件中的 2111412 条记录,未压缩约 6.5GB。

Pandas 0.12 不允许生成器,开发版允许,但将所有生成器放在一个列表中,然后转换为框架。它效率不高,但必须在内部处理熊猫。与此同时,我必须考虑购买更多内存。

【问题讨论】:

  • 问题一定出在tuple_generator,因为像tuple_generator = (item for item in [[1,2,3],[2,3,4,5]])这样的简单生成器表达式不会出现问题。
  • @unutbu 不在 pandas 0.12 上。在开发版本上它可以正常工作。
  • @ViktorKerkez:哦,我明白了。感谢您的信息。
  • 听起来您可能遇到thrashing,在这种情况下,您应该考虑为您的机器添加更多内存。

标签: python python-3.x pandas


【解决方案1】:

您无法使用 0.12 版本的 pandas 从生成器创建 DataFrame。您可以将自己更新到开发版本(从 gi​​thub 获取并编译它 - 这在 Windows 上有点痛苦,但我更喜欢这个选项)。

或者你可以,既然你说你正在过滤这些行,首先过滤它们,将它们写入一个文件,然后使用 read_csv 或其他东西加载它们......

如果你想变得超级复杂,你可以创建一个类似对象的文件,它会返回以下行:

def gen():
    lines = [
        'col1,col2\n',
        'foo,bar\n',
        'foo,baz\n',
        'bar,baz\n'
    ]
    for line in lines:
        yield line

class Reader(object):
    def __init__(self, g):
        self.g = g
    def read(self, n=0):
        try:
            return next(self.g)
        except StopIteration:
            return ''

然后使用read_csv:

>>> pd.read_csv(Reader(gen()))
  col1 col2
0  foo  bar
1  foo  baz
2  bar  baz

【讨论】:

  • 你说得对,pandas 0.12 不支持生成器。我已经安装了开发版本,并且 DataFrame 构造函数允许生成器,但 DataFrame.from_records() 不允许。我已经为它做了一个补丁。
  • @Viktor Kerkez :快速提问,如果我的生成器函数有行中的列表列表,但不一致,说有些对象可能是列表列表,有些可能只是列表,如何我优雅地更改了“读取”方法,还是应该在迭代 gen() 中的行时处理它?
  • @Viktor kerkez :非常基本的问题,但这就是我的意思。如果我定义 lines = [ ['col1,col2\n'], ['foo,bar\n'], ['foo,baz\n'], ['bar,baz\n'] ],那么保持其余相同,我看到 Python shell 重新启动。我还尝试将 Reader 类的 then 对象实例化为 r=Reader(gen()) df=pd.read_csv(r) 。这向我表明,关于类(对象)类型表示法有一些非常基本的东西,我不明白。我的假设是,如果我愿意,我应该被允许在 df“列”内创建列表,而不是 shell-restart。
  • @ekta read_csv 函数只能解析不能包含列表的 "pure" CSV 文件。如果您想在数据框列中使用列表,则必须使用其他东西...解析 json 或手动执行。
  • @ViktorKerkez 您的 Reader() 解决方案对性能有何影响?
【解决方案2】:

为了提高内存效率,请分块读取。像这样,使用上面的 Viktor 的 Reader 类。

df = pd.concat(list(pd.read_csv(Reader(gen()),chunksize=10000)),axis=1)

【讨论】:

  • @Jeff - Reader() 导致 read_csv 和 from_csv() 崩溃 python。这个解决方案还有效吗?
【解决方案3】:

您也可以使用类似(Python 在 2.7.5 中测试)

from itertools import izip

def dataframe_from_row_iterator(row_iterator, colnames):
    col_iterator = izip(*row_iterator)
    return pd.DataFrame({cn: cv for (cn, cv) in izip(colnames, col_iterator)})

您还可以调整它以将行附加到 DataFrame。

-- 编辑,12 月 4 日:s/row/rows 在最后一行

【讨论】:

  • 这与问题中提出的问题相同,将整个数据具体化为除数据框或 numpy 数组或其他一些打包形式之外的任何东西是不可行的。在这里,您将其具体化为 dict。
  • 同意,它确实将数据具体化为字典。但是,您不必一次实现所有;只需消耗生成器的一部分,然后将数据以块的形式附加到 DataFrame 中。只需使用 itertools.islice 从生成器/row_iterator 获取块。
【解决方案4】:

你当然可以从元组生成器构造一个pandas.DataFrame(),从 0.19 版开始(可能更早)。不要使用.from_records();只需使用构造函数,例如:

import pandas as pd
someGenerator = ( (x, chr(x)) for x in range(48,127) )
someDf = pd.DataFrame(someGenerator)

生产:

type(someDf) #pandas.core.frame.DataFrame

someDf.dtypes
#0     int64
#1    object
#dtype: object

someDf.tail(10)
#      0  1
#69  117  u
#70  118  v
#71  119  w
#72  120  x
#73  121  y
#74  122  z
#75  123  {
#76  124  |
#77  125  }
#78  126  ~

【讨论】:

  • 这个问题来自 pandas 根本不允许生成器的地方(0.13 之前)。
  • .from_records() 的用法对于问题的用例是正确的,因为它使用了记录生成器。默认构造函数不清楚生成器将如何解释,是作为记录生成器还是作为列(系列)生成器。
  • 花了一点创造力来处理 CSV 行,但如果其他人遇到同样的问题,我在生成器中使用了for line in lines: yield next(csv.reader([line])。这对我很有用,因为我需要对每一行执行一些清理,并且在 CSV 中需要担心其他条件逻辑。
【解决方案5】:

如果生成器就像DataFrames 的列表,您只需创建一个新的DataFrame 连接列表的元素:

result = pd.concat(list)

最近我遇到了同样的问题。

【讨论】:

    猜你喜欢
    • 2022-09-24
    • 1970-01-01
    • 2017-11-06
    • 2015-03-24
    • 2015-03-28
    • 2021-04-10
    • 2019-04-18
    • 1970-01-01
    • 2021-11-11
    相关资源
    最近更新 更多