Как эффективно преобразовать кадр данных Pandas в массив 3d NumPy? - PullRequest
0 голосов
/ 18 июня 2020

У меня есть большой фрейм данных с DatetimeIndex и несколькими столбцами. Теперь я хотел бы иметь операцию resample_3d, которую можно использовать следующим образом:

index, array = df.resample_3d("1h", fill_value=0)

... и преобразовать фрейм данных

index | A | B | C | D
10:00 | 1 |   | 
10:01 | 1 |   | 
12:00 | 1 |   |
13:00 | 1 |   |

в 3d- NumPy массив формы (3, 2, 4). Первое измерение - это время (которое можно найти в отдельно возвращаемом index), второе измерение - это индекс строки в «группе повторной выборки», а третье измерение - это функции. Размер второго измерения равен максимальному количеству строк в одной группе повторной выборки. Неиспользуемые записи заполняются (например, нулями).

Есть ли такая или подобная функция в Pandas / другой библиотеке или есть способ эффективно реализовать что-то подобное поверх Pandas без особых усилий ?

Я знаю, что могу построить что-то поверх df.resample().apply(list), но это слишком медленно для больших фреймов данных.

Я уже начал свою собственную реализацию с Numba, но затем быстро понял, что это довольно сложная работа.

(Я только что обнаружил xarray и подумал, что помечу им этот вопрос, потому что он может быть лучшей основой для этого, чем Pandas.)

1 Ответ

1 голос
/ 03 июля 2020

Неясно, каковы ваши данные, но да, xarray может быть тем, что вы ищете.

Как только ваши данные будут правильно отформатированы как DataArray, вы можете тогда просто выполните:

da.resample(time="1h")

Он вернет объект DataArrayResample.

Обычно при повторной выборке новая сетка координат не соответствует предыдущей сетке .

Таким образом, оттуда вам нужно применить один из многочисленных методов объекта DataArrayResample , чтобы сообщить xarray, как заполнить эту новую сетку.

Для Например, вы можете интерполировать значения, используя исходные данные в виде узлов:

da.resample(time="1h").interpolate("linear")

Но вы также можете выполнить обратную засыпку, заполнить, использовать ближайшие значения и т. д. c.

Если вы не Если вы не хотите заполнять новую сетку, используйте .asfreq(), и новое время будет установлено на NaN. Вы по-прежнему сможете интерполировать позже, используя interpolate_na().

Ваш случай

В вашем случае кажется, что вы выполняете понижающую выборку и таким образом, существует точное совпадение между новыми координатами сетки и исходными координатами сетки.

Итак, методы, которые будут работать для вас, - это любые из .nearest(), .asfreq(), .interpolate() (обратите внимание, что .interpolate() преобразует int в float).

Однако, поскольку вы выполняете понижающую дискретизацию в точных узлах сетки, на самом деле вы выбираете подмножество своего массива, поэтому вы можете использовать * 1044 Вместо этого используется метод *.sel().

Пример

Пример понижающей дискретизации на точных узлах сетки.

Создание данных:

>>> dims = ("time", "features")
>>> sizes = (6, 3)
>>> h_step = 0.5

>>> da = xr.DataArray(
        dims=dims,
        data=np.arange(np.prod(sizes)).reshape(*sizes),
        coords=dict(
            time=pd.date_range(
                "04/07/2020",
                periods=sizes[0],
                freq=pd.DateOffset(hours=h_step),
            ),
            features=list(string.ascii_uppercase[: sizes[1]]),
        ),
    )

>>> da
<xarray.DataArray (time: 6, features: 3)>
array([[ 0,  1,  2],
       [ 3,  4,  5],
       [ 6,  7,  8],
       [ 9, 10, 11],
       [12, 13, 14],
       [15, 16, 17]])
Coordinates:
  * time      (time) datetime64[ns] 2020-04-07 ... 2020-04-07T02:30:00
  * features  (features) <U1 'A' 'B' 'C'

>>> da.time.values
array(['2020-04-07T00:00:00.000000000',
       '2020-04-07T00:30:00.000000000',
       '2020-04-07T01:00:00.000000000', 
       '2020-04-07T01:30:00.000000000',
       '2020-04-07T02:00:00.000000000',
       '2020-04-07T02:30:00.000000000'],
      dtype='datetime64[ns]')

Даунсэмплинг с использованием .resample() и .nearest():

>>> da.resample(time="1h").nearest()
<xarray.DataArray (time: 3, features: 3)>
array([[ 0,  1,  2],
       [ 6,  7,  8],
       [12, 13, 14]])
Coordinates:
  * time      (time) datetime64[ns] 2020-04-07 ... 2020-04-07T02:00:00
  * features  (features) <U1 'A' 'B' 'C'

>>> da.resample(time="1h").nearest().time.values
array(['2020-04-07T00:00:00.000000000',
       '2020-04-07T01:00:00.000000000',
       '2020-04-07T02:00:00.000000000'],
      dtype='datetime64[ns]')

Понижающая дискретизация по выбору:

>>> dwn_step = 2

>>> new_time = pd.date_range(
        "04/07/2020",
        periods=sizes[0] // dwn_step,
        freq=pd.DateOffset(hours=h_step * dwn_step),
    )

>>> da.sel(time=new_time)
<xarray.DataArray (time: 3, features: 3)>
array([[ 0,  1,  2],
       [ 6,  7,  8],
       [12, 13, 14]])
Coordinates:
  * time      (time) datetime64[ns] 2020-04-07 ... 2020-04-07T02:00:00
  * features  (features) <U1 'A' 'B' 'C'

>>> da.sel(time=new_time).time.values
array(['2020-04-07T00:00:00.000000000',
       '2020-04-07T01:00:00.000000000',
       '2020-04-07T02:00:00.000000000'],
      dtype='datetime64[ns]')

Другой вариант создания индекса new_time - просто выполнить :

new_time = da.time[::dwn_coeff]

Это проще, но вы не можете выбрать первое выбранное время (что может быть либо хорошим, либо плохо, в зависимости от вашего случая).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...