Python:为什么使用asfreq删除我的最后一行数据?

问题描述

我不知道为什么当我使用pandas asfreq()方法时会丢弃我的最后一行数据。

#include <iostream>
#include <memory>

using namespace std;

template <class T>
class Object
{
public:
    T *values = nullptr;
    int size;
    Object(int size) : size(size)
    {
        values = new T[size];
    }

    ~Object()
    {
        delete[] this->values;
    }

    void myFunc(T *&&new_values)
    {
        cout << "myFunc called!" << endl;
        delete[] this->values;
        values = new_values;
    }

    void print()
    {
        for (int i = 0; i < size; i++)
            cout << this->values[i] << " ";
        cout << endl;
    }
};

int main()
{
    auto my_object = new Object<int>(4);
    std::unique_ptr<Object<int>> my_other_object(new Object<int>(4));

    int values[4] = {1,2,3,4};
    int my_other_values[4] = {10,20,30,40};

    /* This works all fine! */
    my_object->myFunc(std::move(values));
    my_object->print();

    /* This next bit throws pointer being freed was not allocated */
    my_other_object->myFunc(std::move(my_other_values));
    my_other_object->print();
}

使用上述代码运行结果:

result with above code

运行结果注释data = rental_2012_09_to_2020_06.copy() data.drop(columns='postcode',inplace=True) data.columns = ['Quarter_End','Rental_Median'] data.index = pd.to_datetime(data['Quarter_End']) data = data.asfreq(freq='Q',method='ffill') data.drop(columns='Quarter_End',inplace=True) data.info() data.head() 行:

result withouth freq()

有什么想法吗?我阅读了文档,但没有提供有关此行为的详细信息。谢谢!

解决方法

您按季度频率对时间序列数据帧进行了重新采样:

data = data.asfreq(freq='Q',method='ffill')

根据pandas documentationQ频率别名代表“季度末频率”。原始数据框中的第一个条目是从2012年9月1日开始,最后一个条目是从2020年6月1日开始。

我猜测asfreq对时间范围进行了重新采样,以使重新采样的范围落在原始范围内(但据我所知,这没有记录)。由于01/09/2012之后的第一季度末为30/09/2012,而01/06/2020之前的最后一个季度末为31/03/2020,因此结果范围为30/09/2012至31/03 / 2020(含两端),每季度一次,产生31个样本。该样本比原始数据帧少一个事实,这只是一个巧合(从某种意义上说,这取决于原始日期时间范围)。

编辑:通过深入研究熊猫的源代码,我找到了定义/记录了此行为的确切位置。深入到asfreq的调用图中,有生成器generate_range(具体是pandas.core.arrays.datetimes.generate_range,从v1.1.2开始),它是定义范围内日期时间值的核心功能。 [start,end],两个值之间有一定的时间偏移(即频率)。

def generate_range(start=None,end=None,periods=None,offset=BDay()): ...

其文档字符串指定:

Notes
-----
* [...]
* If both start and end are specified,the returned dates will
satisfy start <= date <= end.

回复评论

我认为您可能误解了asfreq的所作所为。它不只是简单地将值移动到落在频率边界上的最接近的时间。相反,它使用来自原始数据的数据来创建一个全新的序列,就好像它是在某些时间戳(由频率指定)上采样的一样。也就是说,您是resampling的数据。

尝试从通话中删除method='ffil'参数:您会看到新的系列将全部用NaN(在那些时间戳与原始系列的时间戳重合的情况下除外) —这是因为它不知道在未知时间戳记下采样值应该是什么。

import numpy as np
import pandas as pd
import datetime as dtm

index = pd.DatetimeIndex(data=[
    dtm.datetime(2019,12,1),dtm.datetime(2020,2,17),3,31),6,])

series = pd.Series([1,4],index=index)
>>> series
2019-12-01    1
2020-02-17    2
2020-03-31    3
2020-06-01    4
dtype: int64

>>> series.asfreq('Q')
2019-12-31    NaN
2020-03-31    3.0
Freq: Q-DEC,dtype: float64

如果您考虑一下,这将是很合理的:如果原始数据中没有该日期的记录值,pandas应该如何知道2019-12-31的价值?

当然,拥有一系列NaN并不是很有用,所以我们需要找到一种方法来从可用数据中推断。这就是method='ffil'(复制最后一个可用值)或method='bfil'(复制下一个可用值)之类的填充方法发挥作用的地方。

现在,回到您的问题:2020-06-30没有记录的唯一原因是由于上面generate_range的文档字符串指定了什么:

[...]返回的日期将满足start <= date <= end

即,转换后的日期时间范围将始终落在原始范围内 ,而2020-06-30落在范围内 (因为end为原始范围是2020-06-01)。

同样,考虑到我们刚才讨论的内容,这也很有意义:通常您想推断重新采样的序列中的缺失值(使用原始序列中的值),并且总是更容易(更安全)。 > interpolate (即猜测两个其他值之间的时间步长将是什么),而不是 extrapolate (即猜测在开始之前或之后超出原始范围的值)。结束),并且只有在您将新的重新采样范围保持在原始范围内的情况下,前者才能得到保证-更不用说'ffil''bfil'都需要此条件才能正常工作(您可以t如果第一个值位于start之前,则将其向前填充;如果最后一个值位于end之后,则不能后向填充。)

如果这不是您想要的行为,而是只想更改时间步长,则必须完全执行其他操作。您可以向索引添加偏移量,例如:

index = pd.date_range(
    dtm.datetime(2019,9,freq=pd.DateOffset(months=3)
)

series = pd.Series([1,index=index)
>>> index
DatetimeIndex(['2019-09-01','2019-12-01','2020-03-01','2020-06-01'],dtype='datetime64[ns]',freq='<DateOffset: months=3>')

>>> index + pd.tseries.offsets.MonthEnd()
DatetimeIndex(['2019-09-30','2019-12-31','2020-03-31','2020-06-30'],freq='<DateOffset: months=3>')