可以memmap熊猫系列。那么数据框怎么样?

时间:2017-08-29 15:36:32

标签: python pandas numpy multidimensional-array numpy-memmap

似乎我可以通过创建一个mmap' d ndarray并使用它来初始化Series来为memthon系列的底层数据进行memmap。

        def assert_readonly(iloc):
           try:
               iloc[0] = 999 # Should be non-editable
               raise Exception("MUST BE READ ONLY (1)")
           except ValueError as e:
               assert "read-only" in e.message

        # Original ndarray
        n = 1000
        _arr = np.arange(0,1000, dtype=float)

        # Convert it to a memmap
        mm = np.memmap(filename, mode='w+', shape=_arr.shape, dtype=_arr.dtype)
        mm[:] = _arr[:]
        del _arr
        mm.flush()
        mm.flags['WRITEABLE'] = False  # Make immutable!

        # Wrap as a series
        s = pd.Series(mm, name="a")
        assert_readonly(s.iloc)

成功!似乎s由只读mem映射的ndarray支持。 我可以为DataFrame执行相同的操作吗?以下失败

        df = pd.DataFrame(s, copy=False, columns=['a'])
        assert_readonly(df["a"]) # Fails

以下成功,但仅限于一列:

        df = pd.DataFrame(mm.reshape(len(mm,1)), columns=['a'], copy=False)
        assert_readonly(df["a"]) # Succeeds

...所以我可以制作DF而无需复制。但是,这仅适用于一列,我想要很多。方法I找到了组合1列DF:pd.concat(.. copy = False),pd.merge(copy = False),...结果副本。

我有数千个大型列作为数据文件,其中我一次只需要几个。我希望我能够像上面那样将他们的mmap表示放在DataFrame中。有可能吗?

Pandas文档让人有点难以猜测这里发生了什么 - 尽管它确实说的是DataFrame "Can be thought of as a dict-like container for Series objects."。我开始这就不再是这种情况了。

我不想用HD5来解决这个问题。

2 个答案:

答案 0 :(得分:7)

好的......经过大量的挖掘后,这是怎么回事。 Pandas'DataFrame使用BlockManager类在内部组织数据。与文档相反,DataFrame不是系列的集合,而是类似dtyped矩阵的集合。 BlockManger将所有浮点列组合在一起,将所有int列组合在一起......等等,并将它们的内存(从我能说的内容)保存在一起。

如果只提供单个ndarray矩阵(单一类型),则只能在不复制内存的情况下执行此操作。注意,BlockManager(理论上)也支持在其构造中不复制混合类型数据,因为可能没有必要将此输入复制到相同类型的块中。但是,如果单个矩阵是数据参数,则DataFrame构造函数不会复制。

简而言之,如果您将混合类型或多个数组作为构造函数的输入,或者提供带有单个数组的dict,那么您在Pandas中运气不佳,而DataFrame的默认BlockManager将复制您的数据。

在任何情况下,解决此问题的一种方法是强制BlockManager不按类型合并,而是将每列保持为单独的“块”。所以,用猴子修补魔法......

        from pandas.core.internals import BlockManager
        class BlockManagerUnconsolidated(BlockManager):
            def __init__(self, *args, **kwargs):
                BlockManager.__init__(self, *args, **kwargs)
                self._is_consolidated = False
                self._known_consolidated = False

            def _consolidate_inplace(self): pass
            def _consolidate(self): return self.blocks


        def df_from_arrays(arrays, columns, index):
            from pandas.core.internals import make_block
            def gen():
                _len = None
                p = 0
                for a in arrays:
                    if _len is None:
                        _len = len(a)
                        assert len(index) == _len
                    assert _len == len(a)
                    yield make_block(values=a.reshape((1,_len)), placement=(p,))
                    p+=1

            blocks = tuple(gen())
            mgr = BlockManagerUnconsolidated(blocks=blocks, axes=[columns, index])
            return pd.DataFrame(mgr, copy=False)

如果指定了copy = False,那么如果DataFrame或BlockManger具有consolidate = False(或假设此行为),那会更好。

测试:

    def assert_readonly(iloc):
       try:
           iloc[0] = 999 # Should be non-editable
           raise Exception("MUST BE READ ONLY (1)")
       except ValueError as e:
           assert "read-only" in e.message

    # Original ndarray
    n = 1000
    _arr = np.arange(0,1000, dtype=float)

    # Convert it to a memmap
    mm = np.memmap(filename, mode='w+', shape=_arr.shape, dtype=_arr.dtype)
    mm[:] = _arr[:]
    del _arr
    mm.flush()
    mm.flags['WRITEABLE'] = False  # Make immutable!

        df = df_from_arrays(
            [mm, mm, mm],
            columns=['a', 'b', 'c'],
            index=range(len(mm)))
        assert_read_only(df["a"].iloc)
        assert_read_only(df["b"].iloc)
        assert_read_only(df["c"].iloc)

对于BlockManager是否需要将类似类型的数据保存在一起,是否真的有实际好处似乎有点可疑 - Pandas中的大多数操作都是标签行或每列 - 这个从DataFrame开始,它是异构列的结构,通常只与它们的索引相关联。虽然可行的是他们每个'块'保留一个索引,但如果索引保持偏移到块中就会获益(如果是这种情况,那么它们应该按sizeof(dtype)分组,我认为不是这样的)。 哼哼......

有一些关于PR provide a non-copying constructor的讨论被废弃了。

看起来有明智的plans to phase out BlockManager,所以你的里程很多。

另见Pandas under the hood,这对我帮助很大。

答案 1 :(得分:4)

如果更改DataFrame构造函数以添加参数copy = False,您将获得所需的行为。 https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html

编辑:此外,你想使用底层的ndarray(而不是熊猫系列)。