Может мемап панды серии. А как насчет данных?

Кажется, что я могу запоминать базовые данные для серии 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 (а не серии панд).

Другие вопросы по тегам