可以映射熊猫系列.数据帧呢?



似乎我可以通过创建一个mmap'd ndarray并使用它来初始化该系列来映射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是由只读mem映射的ndarray支持的。 我可以对数据帧执行相同的操作吗?以下失败

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), ...结果副本。

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

Pandas 文档使得猜测这里发生的事情有点困难 - 尽管它确实说数据帧"可以被认为是系列对象的类似字典的容器"。我开始这样做了,情况不再如此。

我宁愿不需要HD5来解决这个问题。

好的......经过大量的挖掘,这是正在发生的事情。

虽然 pandas 在向构造函数提供 copy=False 参数时维护对系列的 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!

它不适用于数据帧:

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不是系列的集合,而是类似 dtype 矩阵的集合。BlockManger将所有浮点列组合在一起,将所有 int 列组合在一起,等等,它们的记忆(据我所知)保持在一起。

只有在提供单个ndarray矩阵(单一类型)的情况下,它才能在不复制内存的情况下做到这一点。请注意,BlockManager(理论上)还支持在其构造中不复制混合类型数据,因为可能不需要将此输入复制到相同类型的分块中。但是,DataFrame构造函数不会仅在单个矩阵是数据参数时才创建副本。

简而言之,如果你有混合类型或多个数组作为构造函数的输入,或者为单个数组提供字典,那么你在 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)分组,我认为情况并非如此><)。

有一些关于 PR 提供非复制构造函数的讨论,但被放弃了。

看起来有明智的计划来逐步淘汰BlockManager,所以你的里程可能会有所不同。

还看到引擎盖下的熊猫,这对我帮助很大。

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

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

最新更新