@@ -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'])
@@ -81,23 +82,57 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
81
82
82
83
# TODO: support datetime dtype here and in widget serialization
83
84
# u: unsigned int, i: signed int, f: float
84
- numeric_kinds = ['u' , 'i' , 'f' ]
85
+
86
+ # ### Process kind ###
87
+ if not kind :
88
+ kind = ()
89
+ elif isinstance (kind , string_types ):
90
+ kind = (kind ,)
91
+
92
+ first_kind = kind [0 ] if kind else None
93
+
94
+ numeric_kinds = {'u' , 'i' , 'f' }
95
+ kind_default_dtypes = {
96
+ 'u' : 'uint32' , 'i' : 'int32' , 'f' : 'float64' , 'O' : 'object' }
85
97
86
98
# Unwrap data types that have a `values` property that might be a numpy
87
99
# array. If this values property is a numeric numpy array then we
88
100
# can take the fast path below
101
+ #
102
+ # Use date_series.to_pydatetime()
103
+ #
89
104
if pd and isinstance (v , (pd .Series , pd .Index )):
90
- v = v .values
105
+ if v .dtype .kind in numeric_kinds :
106
+ # Get the numeric numpy array so we use fast path below
107
+ v = v .values
108
+ elif v .dtype .kind == 'M' :
109
+ # Convert datetime Series/Index to numpy array of datetime's
110
+ if isinstance (v , pd .Series ):
111
+ v = v .dt .to_pydatetime ()
112
+ else :
113
+ v = v .to_pydatetime ()
91
114
92
115
if not isinstance (v , np .ndarray ):
116
+ # v is not homogenous array
93
117
v_list = [to_scalar_or_list (e ) for e in v ]
118
+
119
+ # Lookup dtype for requested kind, if any
120
+ dtype = kind_default_dtypes .get (first_kind , None )
121
+
122
+ # construct new array from list
94
123
new_v = np .array (v_list , order = 'C' , dtype = dtype )
95
124
elif v .dtype .kind in numeric_kinds :
96
- if dtype :
125
+ # v is a homogenous numeric array
126
+ if kind and v .dtype .kind not in kind :
127
+ # Kind(s) were specified and this array doens't match
128
+ # Convert to the default dtype for the first kind
129
+ dtype = kind_default_dtypes .get (first_kind , None )
97
130
new_v = np .ascontiguousarray (v .astype (dtype ))
98
131
else :
132
+ # Either no kind was requested or requested kind is satisfied
99
133
new_v = np .ascontiguousarray (v .copy ())
100
134
else :
135
+ # v is a non-numeric homogenous array
101
136
new_v = v .copy ()
102
137
103
138
# Handle force numeric param
@@ -106,7 +141,7 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
106
141
raise ValueError ('Input value is not numeric and'
107
142
'force_numeric parameter set to True' )
108
143
109
- if dtype != 'unicode' :
144
+ if 'U' not in kind :
110
145
# Force non-numeric arrays to have object type
111
146
# --------------------------------------------
112
147
# Here we make sure that non-numeric arrays have the object
@@ -116,12 +151,6 @@ def copy_to_readonly_numpy_array(v, dtype=None, force_numeric=False):
116
151
if new_v .dtype .kind not in ['u' , 'i' , 'f' , 'O' ]:
117
152
new_v = np .array (v , dtype = 'object' )
118
153
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
154
# Set new array to be read-only
126
155
# -----------------------------
127
156
new_v .flags ['WRITEABLE' ] = False
@@ -749,10 +778,13 @@ def validate_coerce(self, v):
749
778
# Pass None through
750
779
pass
751
780
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
781
755
- v_array = copy_to_readonly_numpy_array (v , dtype = 'int32' )
782
+ v_array = copy_to_readonly_numpy_array (v ,
783
+ kind = ('i' , 'u' ),
784
+ force_numeric = True )
785
+
786
+ if v_array .dtype .kind not in ['i' , 'u' ]:
787
+ self .raise_invalid_val (v )
756
788
757
789
# Check min/max
758
790
if self .has_min_max :
@@ -875,7 +907,7 @@ def validate_coerce(self, v):
875
907
876
908
if is_homogeneous_array (v ):
877
909
# If not strict, let numpy cast elements to strings
878
- v = copy_to_readonly_numpy_array (v , dtype = 'unicode ' )
910
+ v = copy_to_readonly_numpy_array (v , kind = 'U ' )
879
911
880
912
# Check no_blank
881
913
if self .no_blank :
@@ -1057,10 +1089,10 @@ def validate_coerce(self, v, should_raise=True):
1057
1089
# ### Check that elements have valid colors types ###
1058
1090
elif self .numbers_allowed () or invalid_els :
1059
1091
v = copy_to_readonly_numpy_array (
1060
- validated_v , dtype = 'object ' )
1092
+ validated_v , kind = 'O ' )
1061
1093
else :
1062
1094
v = copy_to_readonly_numpy_array (
1063
- validated_v , dtype = 'unicode ' )
1095
+ validated_v , kind = 'U ' )
1064
1096
elif self .array_ok and is_simple_array (v ):
1065
1097
validated_v = [
1066
1098
self .validate_coerce (e , should_raise = False )
@@ -1509,7 +1541,7 @@ def validate_coerce(self, v):
1509
1541
self .raise_invalid_elements (invalid_els )
1510
1542
1511
1543
if is_homogeneous_array (v ):
1512
- v = copy_to_readonly_numpy_array (validated_v , dtype = 'unicode ' )
1544
+ v = copy_to_readonly_numpy_array (validated_v , kind = 'U ' )
1513
1545
else :
1514
1546
v = to_scalar_or_list (v )
1515
1547
else :
@@ -1559,7 +1591,7 @@ def validate_coerce(self, v):
1559
1591
# Pass None through
1560
1592
pass
1561
1593
elif self .array_ok and is_homogeneous_array (v ):
1562
- v = copy_to_readonly_numpy_array (v , dtype = 'object ' )
1594
+ v = copy_to_readonly_numpy_array (v , kind = 'O ' )
1563
1595
elif self .array_ok and is_simple_array (v ):
1564
1596
v = to_scalar_or_list (v )
1565
1597
return v
0 commit comments