问题描述
我不知道为什么当我使用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();
}
使用上述代码运行结果:
运行结果注释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()
行:
有什么想法吗?我阅读了文档,但没有提供有关此行为的详细信息。谢谢!
解决方法
您按季度频率对时间序列数据帧进行了重新采样:
data = data.asfreq(freq='Q',method='ffill')
根据pandas documentation,Q
频率别名代表“季度末频率”。原始数据框中的第一个条目是从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>')