2016-10-12 7 views
0

配列のdatetimesから値を抽出する関数を書いています。私は、この機能をPandas DataFrameまたはnumpy ndarrayで操作したいと考えています。Python/Numpy:ベクトル化とアイテムの型変換の問題

値は、Pythonの日時プロパティと同じ方法で返す必要があります。データフレームのために

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

これは(もより良い方法があるかもしれませんが)applymap()を使用して処理するために合理的に簡単です。私はvectorize()を使用してnumpy ndarraysに対して同じアプローチを試みましたが、問題が発生しています。私が期待していた値の代わりに、非常に大きな整数で終わることもあります。

最初はかなり困惑しましたが、何が起きているのか分かりました。ベクトル化された関数は__get__の代わりにitemを使用して、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()を使用しない代替手段がありますか?

ありがとうございます!

最小限のコード例:PY3で

## 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

エラーが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 

が、私は日時単位を変更した場合、それは私たちがより深くこの掘り下げることができますが、これは最も簡単な解決策であるように思わOK

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

を実行します。

vectorizeは便利な機能です。多次元の繰り返しを容易にします。しかし、それは基本的に

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

である。しかし、我々は反復を使用する必要はありませんのでご注意1次元配列のために:

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

関連する問題