首页 > 解决方案 > 使用 Pandas 在不重叠的全天时间窗口上计算统计功能

问题描述

前言

如何将函数应用于具有非重叠滑动窗口的列表。例如data = {x_1, x_2, ...., x_n},我们应用窗口大小为 2 的 f 来获得{f(x_1,x_2), f(x_3, x_4), ...., f(x_{n-1}, x_n)}.

我知道我可以在分区列表上进行分区和使用 map。但是有没有更有效的方法来处理这个操作,尤其是对于 ndarray 和 dataframe?类似于BlockMapMathematica 的东西。

问题

这样做的最终目标是:假设数据框是一个时间序列,其中包含一天中每个小时的值。如何每天应用一个函数(例如平均值、方差),即具有 24 小时大小的非重叠窗口的函数块图?

编辑1:这是一个返回熊猫数据框的代码:

import pandas as pd
import numpy as np

dat = np.random.uniform(0,10,40)
xpd = pd.DataFrame(dat)

xpd.rename(columns = {0:'new_name'}, inplace = True)
date_rng = pd.date_range(start='1/1/2018 03:00:00', periods=40, freq='H') 
xpd.set_index(date_rng, inplace=True)

我如何计算每天的方差,即从每小时数据,并作为数据框返回。我尝试了以下行,但没有成功:

xpd.groupby(by=lambda x: pd.Series.dt.floor(x, freq='d'))

编辑 2 这行得通,问题似乎解决了:

xpd.groupby(by=lambda x: x.floor('d')).var()

标签: pythonpandasnumpydataframetime-series

解决方案


(编辑:回答何时没有编辑并标题为:在数据框或 ndarray 上映射具有非重叠窗口的函数)。


假设n总是偶数,一种方法是:

def pairwise_map(func, items):
    iterators = [iter(items)] * 2
    return map(func, zip(*iterators))


list(pairwise_map(sum, range(10)))
# [1, 5, 9, 13, 17]

这包括两个步骤:分组分离和映射。可以在 中找到更通用的flyingcircus.base.group_by()分离版本。(免责声明:我是该包的主要作者)。


虽然以上适用于一般情况,但如果您有一个 NumPy 数组arr并且该函数func()是矢量化的,则可以简单地使用:

import numpy as np

arr = np.arange(10)


def func(x, y):
    return x + y


func(arr[::2], arr[1::2])
# array([ 1,  5,  9, 13, 17])

编辑

这可以推广到任何大小,例如:

def pairwise_map(func, items, window=2):
    iterators = [iter(items)] * window
    return map(func, zip(*iterators))


list(pairwise_map(sum, range(10), 3))
# [3, 12, 21]

这显然依赖于func()能够接受正确或可变数量的参数。


同样,对于 NumPy 数组和 NumPy 感知函数:

import numpy as np

arr = np.arange(9)


def func(*args):
    return sum(args)

window = 3
func(*(arr[i::window] for i in range(window)))
# array([ 3, 12, 21])

请注意,这需要len(arr) % window == 0.


对于支持axis关键字(例如np.mean(),np.std()等)的 NumPy 函数,可以简单地使用以下重塑技巧:

import numpy as np

arr = np.arange(56)
window = 8
np.mean(arr.reshape(-1, window), axis=1)
# array([ 3.5, 11.5, 19.5, 27.5, 35.5, 43.5, 51.5])

请注意,这也严格要求len(arr) % window == 0,可以通过例如np.concatenate()在输入末尾填充零来强制执行:

import numpy as np

arr = np.arange(53)
remainder = len(arr) % window
padder = np.zeros(window - remainder if remainder else 0, dtype=arr.dtype)
window = 8
np.mean(np.concatenate((arr, padder)).reshape(-1, window), axis=1)
# array([ 3.5 , 11.5 , 19.5 , 27.5 , 35.5 , 43.5 , 31.25])

推荐阅读