似乎我可以通过创建一个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来解决这个问题。
答案 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(而不是熊猫系列)。