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

时间:2015-01-13 16:19:32

标签: python arrays datetime numpy

我正在寻找一个类似于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)
理想情况下,

会返回5.5,但numpy会引发TypeError: array cannot be safely cast to required type。这周围有一种不错的pythonic方式吗?

4 个答案:

答案 0 :(得分:9)

您可以将它们转换为时间戳(编辑后反映使用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

答案 1 :(得分:2)

numpy.interp() function期望arr1arr2是浮点数的1D序列,即如果要使用,则应将datetime个对象的序列转换为1D浮点序列np.interp()

如果输入数据对所有日期时间对象使用相同的UTC偏移量,则可以通过从所有值中减去参考日期来获得浮点数。如果your input is 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

答案 2 :(得分:1)

如果您的时间戳中需要/秒以内,则这里是rchanganswer的略作编辑的版本(基本上只是一种不同的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

关于时区问题,我无话可说,因为我没有在其他时区进行测试。

答案 3 :(得分:0)

我为@rchang的回答提供补充,以供那些希望在熊猫中做到这一点的人使用。此函数采用包含日期的熊猫系列,并返回一个新系列,其值将在指定日期后转换为“天数”。

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()

似乎可行...

enter image description here