可以memmap pandas系列,那么数据框呢?

kse8i1jr  于 2023-03-28  发布在  其他
关注(0)|答案(2)|浏览(96)

我似乎可以通过创建一个mmap的ndarray并用它初始化Series来memmap python系列的底层数据。

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由只读内存Mapndarray支持。我可以对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而不复制。然而,这只适用于一列,我想要很多列。我发现了一个组合1列DF的方法:pd.concat(..copy=False),pd.merge(copy=False),... result in copies.
我有几千个大的列作为数据文件,我一次只需要其中的几个。我希望我能把它们的mmap表示放在一个DataFrame中,如上所述。这可能吗?
Pandas的文档让我们很难猜出它的内部是什么--尽管它确实说了一个DataFrame "Can be thought of as a dict-like container for Series objects."
我不想用HD 5来解决这个问题。

tct7dpnv

tct7dpnv1#

好吧......经过大量的挖掘,这就是正在发生的事情。
当copy=False参数被提供给构造函数时,pandas会为一个系列维护对supplies数组的引用:

import pandas as pd
import numpy as np

a = np.array([1, 2, 3])  # Let's imagine this our memmap array
s = pd.Series(data=a, copy=False)
assert s.to_numpy() is a  # Yes!

对于DataFrame则不适用:

coldict = dict(col1=a,
               # col2=np.array([1.1, 2.2, 3.3]),  # See below
               # col3=np.array([11, 12, 13])
               )
df = pd.DataFrame(data=coldict, copy=False)
assert df["col1"].to_numpy() is a      # Nope! Not even for pandas >=1.3
assert df["col1"].values is a          # Nope!

Pandas的DataFrame使用BlockManager类在内部组织数据。与文档相反,DataFrame不是一个系列的集合,而是一个 * 类似dtyped矩阵的集合 *。BlockManger将所有float列组合在一起,所有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,则DataFrameBlockManger具有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)分组,我不认为是这种情况)。
呵呵……
有一些关于provide a non-copying constructor的PR的讨论,但被放弃了。
看起来有合理的plans to phase out BlockManager,所以你的里程可能会有所不同。
Pandas under the hood,这对我很有帮助。

46scxncf

46scxncf2#

如果您更改DataFrame构造函数以添加参数copy=False,则将获得所需的行为。https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
编辑:此外,您希望使用底层ndarray(而不是pandas系列)。

相关问题