2016-10-12 10 views
0

Ich schreibe eine Funktion, um Werte aus Datumsangaben über Arrays zu extrahieren. Ich möchte, dass die Funktion auf einem Pandas DataFrame oder einem numpy ndarray arbeitet.Python/Numpy: Probleme mit der Typkonvertierung in Vektorisieren und Element

Die Werte sollten auf die gleiche Weise zurückgegeben werden wie die Python-Datetime-Eigenschaften, z.

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

Für einen Datenrahmen ist dies relativ einfach zu handhaben mit applymap() (obwohl es auch einen besseren Weg sein kann). Ich habe den gleichen Ansatz für numpy ndarrays mit vectorize() versucht, und ich stoße auf Probleme. Statt der Werte, die ich erwartet hatte, habe ich sehr große Zahlen, manchmal negative.

Das war ziemlich zunächst rätselhaft, aber ich herausgefunden, was passiert ist: wird mit item statt __get__ bekommen die vektorisiert Funktion die Werte aus dem ndarray. Dies scheint automatisch jedes datetime64 Objekt in ein long zu konvertieren:

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

Die lange scheint die Zahl der ns seit Epoche zu sein (1970-01-01T00: 00: 00). Irgendwo entlang der Linie werden die Werte in ganze Zahlen umgewandelt und sie überlaufen, daher die negativen Zahlen.

Das ist also das Problem. Kann mir bitte jemand helfen, es zu reparieren? Das einzige, was ich mir vorstellen kann, ist die manuelle Konvertierung, aber das würde bedeuten, einen Teil des datetime Moduls neu zu implementieren.

Gibt es eine Alternative zu vectorize, die item() nicht verwendet?

Danke!

Minimal Codebeispiel:

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

Antwort

2

In PY3 der Fehler 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 

aber wenn ich die Datetime-Einheiten zu ändern, läuft es ok

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

wir in diese in mehr Tiefe graben könnte, aber dies scheint einfachste Lösung zu sein.

Denken Sie daran, dass vectorize eine Komfortfunktion ist; Es erleichtert das Iterieren über Multidimensions. Aber für ein 1D-Array ist es im Grunde

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

Aber beachten Sie, dass wir müssen Iteration nicht verwenden:

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

Dank @hpaulj, Sie machen einen ausgezeichneten Punkt, um dort für vectorize keine Notwendigkeit zu sein - benutze 'astype ('datetime64 [$ UNIT]')' (hoffentlich kannst du meine fürchterlich gemischte Syntax dort durchsehen) auf dem ganzen Array funktioniert ein Leckerbissen – charrison

Verwandte Themen