Может мемап панды серии. А как насчет данных?
Кажется, что я могу запоминать базовые данные для серии Python, создав mmap'd ndarray и используя его для инициализации Series.
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
поддерживается только для чтения мем-сопоставленных 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 без копирования. Тем не менее, это работает только для одного столбца, и я хочу много. Метод, который я нашел для объединения 1-столбцов DF: pd.concat(..copy=False), pd.merge(copy=False), ... приводит к копированию.
У меня есть несколько тысяч больших столбцов в виде файлов данных, из которых мне нужно всего лишь несколько одновременно. Я надеялся, что смогу разместить их представления mmap в DataFrame, как описано выше. Является ли это возможным?
Документация Pandas делает немного трудным догадаться о том, что здесь происходит под капотом - хотя он и говорит, что DataFrame "может рассматриваться как диктовочный контейнер для объектов Series"., Я начинаю это, это уже не так.
Я предпочел бы не нуждаться в HD5, чтобы решить это.
2 ответа
ОК... после долгих раскопок, вот что происходит. Панды DataFrame
использует BlockManager
класс для организации данных внутри страны. В отличие от документов, DataFrame
это не коллекция серий, а коллекция матриц с одинаковыми типами. BlockManger группирует все столбцы с плавающей точкой вместе, все столбцы int вместе и т. Д., И их память (насколько я могу судить) хранится вместе.
Это можно сделать без копирования памяти ТОЛЬКО если один ndarray
матрица (один тип) предоставляется. Обратите внимание, что BlockManager (в теории) также поддерживает не-копирование данных смешанного типа в своей конструкции, так как может не потребоваться копировать эти входные данные в блоки одного типа. Однако конструктор DataFrame не делает копию ТОЛЬКО, если в качестве параметра данных используется одна матрица.
Короче говоря, если у вас есть смешанные типы или несколько массивов в качестве входных данных для конструктора, или если вы предоставляете команду с одним массивом, вам не повезло в Pandas, и BlockManager по умолчанию в DataFrame скопирует ваши данные.
В любом случае, один из способов обойти это - заставить 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)
Было бы лучше, если бы DataFrame или BlockManger имели объединение =False (или предполагали такое поведение), если было указано copy=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, поэтому ваш пробег может варьироваться.
Также вижу панд под капотом, что мне очень помогло.
Если вы измените свой конструктор DataFrame, добавив параметр copy=False, вы получите желаемое поведение. https://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.html
Изменить: Кроме того, вы хотите использовать базовый ndarray (а не серии панд).