added support for numpy arrays
This commit is contained in:
@ -1,3 +1,5 @@
|
|||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
#PV data type: enum, string, char, float or int
|
#PV data type: enum, string, char, float or int
|
||||||
DTYPES = {
|
DTYPES = {
|
||||||
@ -14,14 +16,32 @@ class PVInfo:
|
|||||||
|
|
||||||
def __init__(self, name, data, parse_string=True):
|
def __init__(self, name, data, parse_string=True):
|
||||||
self.name = name.upper()
|
self.name = name.upper()
|
||||||
self.dtype, self.value = infer_type(data, parse_string=parse_string)
|
#TODO: the following is ugly
|
||||||
|
res = infer_type(data, parse_string=parse_string)
|
||||||
|
if len(res) == 2:
|
||||||
|
self.dtype, self.value = res
|
||||||
|
self.count = 1
|
||||||
|
elif len(res) == 3:
|
||||||
|
self.dtype, self.value, self.count = res
|
||||||
|
|
||||||
def to_dict(self): #TODO count for arrays
|
def to_dict(self):
|
||||||
return {"type": self.dtype}
|
#TODO: the following is ugly
|
||||||
|
if self.count == 1:
|
||||||
|
return {"type": self.dtype}
|
||||||
|
else:
|
||||||
|
return {"type": self.dtype, "count": self.count}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def infer_type(value, parse_string=True): #TODO arrays? strings?
|
def infer_type(value, parse_string=True): #TODO lists/tuples/...? char vs. strings?
|
||||||
|
if isinstance(value, np.ndarray):
|
||||||
|
return infer_type_numpy(value) #TODO: also parse strings for np arrays?!
|
||||||
|
else:
|
||||||
|
return infer_type_scalar(value, parse_string=parse_string)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def infer_type_scalar(value, parse_string=True):
|
||||||
if parse_string and isinstance(value, str):
|
if parse_string and isinstance(value, str):
|
||||||
if value in STR_REPS:
|
if value in STR_REPS:
|
||||||
value = STR_REPS[value]
|
value = STR_REPS[value]
|
||||||
@ -32,12 +52,14 @@ def infer_type(value, parse_string=True): #TODO arrays? strings?
|
|||||||
else:
|
else:
|
||||||
dtype = type(value)
|
dtype = type(value)
|
||||||
|
|
||||||
|
#TODO: this can probably be removed
|
||||||
if isinstance(dtype, str):
|
if isinstance(dtype, str):
|
||||||
raise Exception("how?")
|
raise Exception("how?")
|
||||||
|
|
||||||
if dtype in DTYPES:
|
if dtype in DTYPES:
|
||||||
dtype = DTYPES[dtype]
|
dtype = DTYPES[dtype]
|
||||||
else:
|
else:
|
||||||
|
# if dtype not understood, default to string and use string representation
|
||||||
dtype = "string"
|
dtype = "string"
|
||||||
value = str(value)
|
value = str(value)
|
||||||
|
|
||||||
@ -45,3 +67,20 @@ def infer_type(value, parse_string=True): #TODO arrays? strings?
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def infer_type_numpy(arr):
|
||||||
|
if arr.dtype == object:
|
||||||
|
raise NotImplementedError("cannot infer a single type from object arrays")
|
||||||
|
|
||||||
|
# bool -> int
|
||||||
|
if arr.dtype == bool:
|
||||||
|
arr = arr.astype(int)
|
||||||
|
|
||||||
|
# epics only allows 1D, and needs conversion to built-in data types
|
||||||
|
lst = arr.ravel().tolist()
|
||||||
|
dtype, _ = infer_type_scalar(lst[0])
|
||||||
|
count = len(lst)
|
||||||
|
|
||||||
|
return dtype, lst, count
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from math import nan
|
from math import nan
|
||||||
|
import numpy as np
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
from pvinfo import infer_type as it
|
from pvinfo import infer_type as it
|
||||||
@ -60,11 +62,48 @@ def test_psfalse_int():
|
|||||||
assert psfalse("int") == ("string", "int")
|
assert psfalse("int") == ("string", "int")
|
||||||
|
|
||||||
|
|
||||||
def test_it_nan():
|
|
||||||
assert it(nan) == ("float", nan)
|
|
||||||
|
|
||||||
def test_it_None():
|
def test_it_None():
|
||||||
assert it(None) == ("string", "None")
|
assert it(None) == ("string", "None")
|
||||||
|
|
||||||
|
def test_it_True():
|
||||||
|
assert it(True) == ("string", "True")
|
||||||
|
|
||||||
|
def test_it_False():
|
||||||
|
assert it(False) == ("string", "False")
|
||||||
|
|
||||||
|
def test_it_nan():
|
||||||
|
assert it(nan) == ("float", nan)
|
||||||
|
|
||||||
|
def test_it_np_nan():
|
||||||
|
assert it(np.nan) == ("float", np.nan)
|
||||||
|
|
||||||
|
|
||||||
|
def test_it_np1D_int():
|
||||||
|
n = 10
|
||||||
|
arr = np.arange(n)
|
||||||
|
ref = arr.tolist()
|
||||||
|
assert it(arr) == ("int", ref, n)
|
||||||
|
|
||||||
|
def test_it_np2D_int():
|
||||||
|
x = y = 10
|
||||||
|
n = x * y
|
||||||
|
arr = np.arange(n)
|
||||||
|
ref = arr.tolist()
|
||||||
|
arr = arr.reshape(x, y)
|
||||||
|
assert it(arr) == ("int", ref, n)
|
||||||
|
|
||||||
|
def test_it_np1D_bool():
|
||||||
|
arr = np.array([True, False, True])
|
||||||
|
ref = [1, 0, 1]
|
||||||
|
assert it(arr) == ("int", ref, len(ref))
|
||||||
|
|
||||||
|
def test_it_np1D_object():
|
||||||
|
arr = np.array([None]) # gives dtype=object
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
it(arr)
|
||||||
|
arr = np.array(["test", 123, 1.23], dtype=object)
|
||||||
|
with pytest.raises(NotImplementedError):
|
||||||
|
it(arr)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user