如何从包含日期时间对象的数组中进行插值?

How do you interpolate from an array containing datetime objects?

我正在寻找一个类似于 np.interp 的函数,它可以处理 datetime 个对象。

例如:

import datetime, numpy as np
arr1 = np.array([datetime.datetime(2008,1,d) for d in range(1,10)])
arr2 = np.arange(1,10)

np.interp(datetime.datetime(2008,1,5,12),arr1,arr2)

理想情况下 return 5.5,但 numpy 加注 TypeError: array cannot be safely cast to required type。有没有很好的 pythonic 方法解决这个问题?

您可以将它们转换为时间戳(已编辑以反映 calendar.timegm 的使用以避免与时区相关的陷阱)。

# Python 2.7
import datetime, numpy as np
import calendar

def toTimestamp(d):
  return calendar.timegm(d.timetuple())

arr1 = np.array([toTimestamp(datetime.datetime(2008,1,d)) for d in range(1,10)]) 
arr2 = np.arange(1,10)

result = np.interp(toTimestamp(datetime.datetime(2008,1,5,12)),arr1,arr2)
print result # Prints 5.5

numpy.interp() function 期望 arr1arr2 是浮点数的一维序列,即,如果需要,您应该将 datetime 对象的序列转换为浮点数的一维序列使用 np.interp().

如果输入数据对所有日期时间对象使用相同的 UTC 偏移量,那么您可以通过从所有值中减去参考日期来获得浮点数。如果 为真(偏移量始终为零):

from datetime import datetime
import numpy as np

arr1 = np.array([datetime(2008, 1, d) for d in range(1, 10)])
arr2 = np.arange(1, 10)

def to_float(d, epoch=arr1[0]):
    return (d - epoch).total_seconds()

f = np.interp(to_float(datetime(2008,1,5,12)), map(to_float, arr1), arr2)
print f # -> 5.5

我提供这个作为对@rchang 对那些想要在 Pandas 中完成所有这些的人的回答的补充。此函数采用包含日期的 pandas 系列和 returns 一个新系列,其值在指定日期后转换为 'number of days'。

def convert_dates_to_days(dates, start_date=None, name='Day'):
    """Converts a series of dates to a series of float values that
    represent days since start_date.
    """

    if start_date:
        ts0 = pd.Timestamp(start_date).timestamp()
    else:
        ts0 = 0

    return ((dates.apply(pd.Timestamp.timestamp) - 
            ts0)/(24*3600)).rename(name)

不确定它是否适用于时间,或者它是否不受上述时区陷阱的影响。但我认为只要您提供同一时区的开始日期,从所有时间戳值中减去该日期,就可以了。

以下是我的使用方法:

from scipy.interpolate import interp1d

data = pd.DataFrame({
    'Date': pd.date_range('2018-01-01', '2018-01-22', freq='7D'),
    'Value': np.random.randn(4)
})

x = convert_dates_to_days(data.Date, start_date='2018-01-01')
y = data.Value
f2 = interp1d(x, y, kind='cubic')

all_dates = pd.Series(pd.date_range('2018-01-01', '2018-01-22'))
x_all = convert_dates_to_days(all_dates, start_date='2018-01-01')

plt.plot(all_dates, f2(x_all), '-')
data.set_index('Date')['Value'].plot(style='o')
plt.grid()
plt.savefig("interp_demo.png")
plt.show()

似乎有效...

如果您的时间戳 have/need 亚秒级精度,这里是 rchang's 的略微编辑版本(基本上只是一种不同的 toTimestamp 方法)

import datetime, numpy as np

def toTimestamp(d):
  return d.timestamp()

arr1 = np.array([toTimestamp(datetime.datetime(2000,1,2,3,4,5) + datetime.timedelta(0,d)) for d in np.linspace(0,1,9)]) 
arr2 = np.arange(1,10) # 1, 2, ..., 9

result = np.interp(toTimestamp(datetime.datetime(2000,1,2,3,4,5,678901)),arr1,arr2)
print(result) # Prints 6.431207656860352

关于时区问题我不能说什么,因为我还没有用其他时区测试过。