@@ -52,17 +52,18 @@ def to_scalar_or_list(v):
52
52
return v
53
53
54
54
55
- def copy_to_readonly_numpy_array (v , dtype = None , force_numeric = False ):
55
+ def copy_to_readonly_numpy_array (v , kind = None , force_numeric = False ):
56
56
"""
57
57
Convert an array-like value into a read-only numpy array
58
58
59
59
Parameters
60
60
----------
61
61
v : array like
62
62
Array like value (list, tuple, numpy array, pandas series, etc.)
63
- dtype : str
64
- If specified, the numpy dtype that the array should be forced to
65
- have. If not specified then let numpy infer the datatype
63
+ kind : str or tuple of str
64
+ If specified, the numpy dtype kind (or kinds) that the array should
65
+ have, or be converted to if possible.
66
+ If not specified then let numpy infer the datatype
66
67
force_numeric : bool
67
68
If true, raise an exception if the resulting numpy array does not
68
69
have a numeric dtype (i.e. dtype.kind not in ['u', 'i', 'f'])
@@ -74,30 +75,53 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
74
75
75
76
assert np is not None
76
77
77
- # Copy to numpy array and handle dtype param
78
- # ------------------------------------------
79
- # If dtype was not specified then it will be passed to the numpy array
80
- # constructor as None and the data type will be inferred automatically
78
+ # ### Process kind ###
79
+ if not kind :
80
+ kind = ()
81
+ elif isinstance (kind , string_types ):
82
+ kind = (kind ,)
83
+
84
+ first_kind = kind [0 ] if kind else None
81
85
82
- # TODO: support datetime dtype here and in widget serialization
83
86
# u: unsigned int, i: signed int, f: float
84
- numeric_kinds = ['u' , 'i' , 'f' ]
87
+ numeric_kinds = {'u' , 'i' , 'f' }
88
+ kind_default_dtypes = {
89
+ 'u' : 'uint32' , 'i' : 'int32' , 'f' : 'float64' , 'O' : 'object' }
85
90
86
- # Unwrap data types that have a `values` property that might be a numpy
87
- # array. If this values property is a numeric numpy array then we
88
- # can take the fast path below
91
+ # Handle pandas Series and Index objects
89
92
if pd and isinstance (v , (pd .Series , pd .Index )):
90
- v = v .values
93
+ if v .dtype .kind in numeric_kinds :
94
+ # Get the numeric numpy array so we use fast path below
95
+ v = v .values
96
+ elif v .dtype .kind == 'M' :
97
+ # Convert datetime Series/Index to numpy array of datetimes
98
+ if isinstance (v , pd .Series ):
99
+ v = v .dt .to_pydatetime ()
100
+ else :
101
+ # DatetimeIndex
102
+ v = v .to_pydatetime ()
91
103
92
104
if not isinstance (v , np .ndarray ):
105
+ # v is not homogenous array
93
106
v_list = [to_scalar_or_list (e ) for e in v ]
107
+
108
+ # Lookup dtype for requested kind, if any
109
+ dtype = kind_default_dtypes .get (first_kind , None )
110
+
111
+ # construct new array from list
94
112
new_v = np .array (v_list , order = 'C' , dtype = dtype )
95
113
elif v .dtype .kind in numeric_kinds :
96
- if dtype :
114
+ # v is a homogenous numeric array
115
+ if kind and v .dtype .kind not in kind :
116
+ # Kind(s) were specified and this array doesn't match
117
+ # Convert to the default dtype for the first kind
118
+ dtype = kind_default_dtypes .get (first_kind , None )
97
119
new_v = np .ascontiguousarray (v .astype (dtype ))
98
120
else :
121
+ # Either no kind was requested or requested kind is satisfied
99
122
new_v = np .ascontiguousarray (v .copy ())
100
123
else :
124
+ # v is a non-numeric homogenous array
101
125
new_v = v .copy ()
102
126
103
127
# Handle force numeric param
@@ -106,7 +130,7 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
106
130
raise ValueError ('Input value is not numeric and'
107
131
'force_numeric parameter set to True' )
108
132
109
- if dtype != 'unicode' :
133
+ if 'U' not in kind :
110
134
# Force non-numeric arrays to have object type
111
135
# --------------------------------------------
112
136
# Here we make sure that non-numeric arrays have the object
@@ -116,12 +140,6 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
116
140
if new_v .dtype .kind not in ['u' , 'i' , 'f' , 'O' ]:
117
141
new_v = np .array (v , dtype = 'object' )
118
142
119
- # Convert int64 arrays to int32
120
- # -----------------------------
121
- # JavaScript doesn't support int64 typed arrays
122
- if new_v .dtype == 'int64' :
123
- new_v = new_v .astype ('int32' )
124
-
125
143
# Set new array to be read-only
126
144
# -----------------------------
127
145
new_v .flags ['WRITEABLE' ] = False
@@ -749,10 +767,13 @@ def validate_coerce(self, v):
749
767
# Pass None through
750
768
pass
751
769
elif self .array_ok and is_homogeneous_array (v ):
752
- if v .dtype .kind not in ['i' , 'u' ]:
753
- self .raise_invalid_val (v )
754
770
755
- v_array = copy_to_readonly_numpy_array (v , dtype = 'int32' )
771
+ v_array = copy_to_readonly_numpy_array (v ,
772
+ kind = ('i' , 'u' ),
773
+ force_numeric = True )
774
+
775
+ if v_array .dtype .kind not in ['i' , 'u' ]:
776
+ self .raise_invalid_val (v )
756
777
757
778
# Check min/max
758
779
if self .has_min_max :
@@ -875,7 +896,7 @@ def validate_coerce(self, v):
875
896
876
897
if is_homogeneous_array (v ):
877
898
# If not strict, let numpy cast elements to strings
878
- v = copy_to_readonly_numpy_array (v , dtype = 'unicode ' )
899
+ v = copy_to_readonly_numpy_array (v , kind = 'U ' )
879
900
880
901
# Check no_blank
881
902
if self .no_blank :
@@ -1057,10 +1078,10 @@ def validate_coerce(self, v, should_raise=True):
1057
1078
# ### Check that elements have valid colors types ###
1058
1079
elif self .numbers_allowed () or invalid_els :
1059
1080
v = copy_to_readonly_numpy_array (
1060
- validated_v , dtype = 'object ' )
1081
+ validated_v , kind = 'O ' )
1061
1082
else :
1062
1083
v = copy_to_readonly_numpy_array (
1063
- validated_v , dtype = 'unicode ' )
1084
+ validated_v , kind = 'U ' )
1064
1085
elif self .array_ok and is_simple_array (v ):
1065
1086
validated_v = [
1066
1087
self .validate_coerce (e , should_raise = False )
@@ -1509,7 +1530,7 @@ def validate_coerce(self, v):
1509
1530
self .raise_invalid_elements (invalid_els )
1510
1531
1511
1532
if is_homogeneous_array (v ):
1512
- v = copy_to_readonly_numpy_array (validated_v , dtype = 'unicode ' )
1533
+ v = copy_to_readonly_numpy_array (validated_v , kind = 'U ' )
1513
1534
else :
1514
1535
v = to_scalar_or_list (v )
1515
1536
else :
@@ -1559,7 +1580,7 @@ def validate_coerce(self, v):
1559
1580
# Pass None through
1560
1581
pass
1561
1582
elif self .array_ok and is_homogeneous_array (v ):
1562
- v = copy_to_readonly_numpy_array (v , dtype = 'object ' )
1583
+ v = copy_to_readonly_numpy_array (v , kind = 'O ' )
1563
1584
elif self .array_ok and is_simple_array (v ):
1564
1585
v = to_scalar_or_list (v )
1565
1586
return v
0 commit comments