2016-10-12 29 views
0

我正在寫一個函數來從數組中提取datetimes中的值。我希望函數能夠在Pandas DataFrame或numpy ndarray上運行。Python/Numpy:向量化和項目中類型轉換的問題

這些值應該以與Python datetime屬性相同的方式返回,例如,

from datetime import datetime 
dt = datetime(2016, 10, 12, 13) 
dt.year 
    => 2016 
dt.second 
    => 0 

對於一個數據幀,這是相當容易使用applymap()(雖然有可能是一個更好的方法)來處理。我嘗試了使用vectorize()的numpy ndarrays的相同方法,並且遇到了問題。取而代之的是我期待的價值觀,我最終得到的是非常大的整數,有時候是負值。

起初,這很令人費解,但我知道發生了什麼事情:矢量化函數使用item而不是__get__將值從ndarray中取出。這似乎自動每個datetime64對象轉換爲long

nd[1][0] 
    => numpy.datetime64('1986-01-15T12:00:00.000000000') 
nd[1].item() 
    => 506174400000000000L 

長似乎是因爲時代的納秒數(1970-01-01T00:00:00)。沿着該行的某個值被轉換爲整數,並且它們溢出,因此負數。

所以這就是問題所在。請有人可以幫我修復它嗎?我能想到的唯一辦法是手動進行轉換,但這實際上意味着要重新實現一大塊datetime模塊。

是否有一些替代vectorize不使用item()

謝謝!

最小代碼例如:

## DataFrame works fine 
import pandas as pd 
from datetime import datetime 

df = pd.DataFrame({'dts': [datetime(1970, 1, 1, 1), datetime(1986, 1, 15, 12), 
         datetime(2016, 7, 15, 23)]}) 
exp = pd.DataFrame({'dts': [1, 15, 15]}) 

df_func = lambda x: x.day  
out = df.applymap(df_func) 

assert out.equals(exp) 

## numpy ndarray is more difficult 
from numpy import datetime64 as dt64, timedelta64 as td64, vectorize # for brevity 

# The unary function is a little more complex, especially for days and months where the minimum value is 1 
nd_func = lambda x: int((dt64(x, 'D') - dt64(x, 'M') + td64(1, 'D'))/td64(1, 'D')) 

nd = df.as_matrix() 
exp = exp.as_matrix() 
    => array([[ 1], 
      [15], 
      [15]]) 

# The function works as expected on a single element... 
assert nd_func(nd[1][0]) == 15 

# ...but not on an ndarray 
nd_vect = vectorize(nd_func) 
out = nd_vect(nd) 
    => array([[ -105972749999999], 
      [ 3546551532709551616], 
      [-6338201187830896640]]) 

回答

2

在PY3誤差OverflowError: Python int too large to convert to C long

In [215]: f=np.vectorize(nd_func,otypes=[int]) 
In [216]: f(dts) 
... 
OverflowError: Python int too large to convert to C long 

但如果我更改了日期時間單位,它運行正常

In [217]: f(dts.astype('datetime64[ms]')) 
Out[217]: array([ 1, 15, 15]) 

我們可以挖掘到這一點更深入,但是這似乎是最簡單的解決方案。

請記住,vectorize是一種方便的功能;它使得遍歷多維更容易。但對於一維數組它基本上是

np.array([nd_func(i) for i in dts]) 

但是請注意,我們沒有使用迭代:

In [227]: (dts.astype('datetime64[D]') - dts.astype('datetime64[M]') + td64(1,'D'))/td64(1,'D').astype(int) 
Out[227]: array([ 1, 15, 15], dtype='timedelta64[D]') 
+0

感謝@hpaulj,你成爲一個優秀的點約因此不需要對矢量化 - 使用'astype('datetime64 [$ UNIT]')'(希望你可以在那裏看到我可怕的混合語法) – charrison