This module defines typecasting and conversion functions for easily manipulating and generating data. The most powerful methods are `cast` and `convert`. Other utilities include `RNG` and `fill`, as well as extensions of binary logic to continuous variables such as `XOR`, and encodings such as gray code.
C:\Anaconda3\lib\site-packages\numpy\_distributor_init.py:32: UserWarning: loaded more than 1 DLL from .libs:
C:\Anaconda3\lib\site-packages\numpy\.libs\libopenblas.NOIJJG62EMASZI6NYURL6JBKM4EVBGM7.gfortran-win_amd64.dll
C:\Anaconda3\lib\site-packages\numpy\.libs\libopenblas.PYQHXLVVQ7VESDPUVUADXEVJOBGHJPAY.gfortran-win_amd64.dll
  stacklevel=1)

Type casting

In order to build the modular typecasting callable cast, we start by outlining the data types and default conversion behavior of python, before extending it to wrap non-iterable objects in the desired iterable, and convert the elements of iterable objects to the desired non-iterable type (shown by examples).

data = Union[None,int,float,list,tuple,str,dict,set,np.ndarray]

trycast[source]

trycast(obj:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray], to:type)

Attempts to typecast obj to datatype to using default type conversion. Fallback of more complex casting cases in this module.

trycast(0.5,int) #works like int(0.5)
0
trycast(0,list) #just return 0 as list(0) gives an error - we want an actual list!
0

The default typecasting, as in the above, has some obvious limitations which can be extended with a basic consideration of whether the object is to be treated as an iterable or a non-iterable. To that end,

nonitr2itr[source]

nonitr2itr(obj:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray], to:type)

Wrap the noniterable obj into the iterable type to.

nonitr2itr(0,list)
[0]

itr2nonitr[source]

itr2nonitr(obj:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray], to:type)

Treat each element of the iterable obj as the noniterable type to.

itr2nonitr([0.5,1.5],int)
[0, 1]

itr2itr[source]

itr2itr(obj:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray], to:type)

Treat the iterable obj as a new iterable of type to. Treats dictionaries as their items rather than default keys, and treats np.ndarray as the callable np.array(obj).

itr2itr({0:1},list) #simply treats dict -> itrtype as dict.items() -> itrtype, as list(dict) returns dict.keys()
[(0, 1)]
itr2itr([0,1],np.ndarray) #makes sure the datatype np.ndarray yields the callable np.array()
array([0, 1])

Now, we combine these conversion cases into a single object, the Caster class, which instantiates as a callable dictionary of partially evaluated functions, that typecast according to the type of the object they are called on, and the type to be casted to. The ruleset is stored as a dictionary which can be modified in place to treat objects differently as needed.

class Caster[source]

Caster(rules:Optional[dict]=None)

Universal typecasting class with customizable ruleset. Stores the ruleset as a dictionary of dictionaries. The outer dictionary stores the type of the object to be converted. The inner dictionary stores all the types to convert into. The values are partially evaluated functions on the inner type, which get called by a class object and evaluated on the outer type. The ruleset can be updated and changed as needed.

cast=Caster()

cast wraps non-iterables in iterables:

cast(0,list)
[0]

cast converts the elements of an iterable into the desired non-iterable:

cast([0.5,1.5],int)
[0, 1]

cast accepts multiple arguments for sequential conversion:

cast([0,1.1,1.5,2],int,set,list) #convert to int, get unique elements, return as list
[0, 1, 2]

By default, we choose to treat strings as non-iterables. This proves useful in later applications of the module.

cast('hi',list)
['hi']

If we want to change this, we can do so by examining how cast converts objects:

cast[str]
{None: functools.partial(<function trycast at 0x00000273E43FD318>, to=None),
 int: functools.partial(<function trycast at 0x00000273E43FD318>, to=<class 'int'>),
 float: functools.partial(<function trycast at 0x00000273E43FD318>, to=<class 'float'>),
 list: functools.partial(<function nonitr2itr at 0x00000273E51B8828>, to=<class 'list'>),
 tuple: functools.partial(<function nonitr2itr at 0x00000273E51B8828>, to=<class 'tuple'>),
 str: functools.partial(<function trycast at 0x00000273E43FD318>, to=<class 'str'>),
 dict: functools.partial(<function nonitr2itr at 0x00000273E51B8828>, to=<class 'dict'>),
 set: functools.partial(<function nonitr2itr at 0x00000273E51B8828>, to=<class 'set'>),
 numpy.ndarray: functools.partial(<function nonitr2itr at 0x00000273E51B8828>, to=<class 'numpy.ndarray'>)}

It is a nested dictionary, keyed by the datatype to be converted, and valued by an inner dict, which is keyed by the datatype to convert to, and valued with a function converting from the outer key to the inner key. To change how the instance evaluates strings to lists for example,

cast[str][list] = lambda s: list(s)
cast('hi',list)
['h', 'i']

If instead we want to change the default behavior of strings to be treated as an iterable, then we can modify the Caster class iterables:

Caster.iterables
[list, tuple, dict, set, numpy.ndarray]
Caster.iterables.append(str)

Now we re-instantiate a class instance,

cast=Caster()
cast('hi',tuple)
('h', 'i')

and strings are treated as iterables, as in native python types. This process can be applied to any datatype, include new ones, etc. We can change it back again by:

Caster.iterables.remove(str)
cast=Caster()

Now let's see all the types of data conversions, with a parsing function for easy printing of datatypes:

typestr[source]

typestr(x:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray])

Parses the string of the input type for readability.

for n in [0,1.5]:
    for t in Caster.noniterables:
        print(f'From {n} to {typestr(t)}: {cast(n,t)}')
From 0 to NoneType: 0
From 0 to int: 0
From 0 to float: 0.0
From 0 to str: 0
From 1.5 to NoneType: 1.5
From 1.5 to int: 1
From 1.5 to float: 1.5
From 1.5 to str: 1.5
for n in [1.5,'1.5']:
    for t in Caster.iterables:
        print(f'From {n} to {typestr(t)}: {cast(n,t)}')
From 1.5 to list: [1.5]
From 1.5 to tuple: (1.5,)
From 1.5 to dict: {1.5: 1.5}
From 1.5 to set: {1.5}
From 1.5 to numpy.ndarray: 1.5
From 1.5 to list: ['1.5']
From 1.5 to tuple: ('1.5',)
From 1.5 to dict: {'1.5': '1.5'}
From 1.5 to set: {'1.5'}
From 1.5 to numpy.ndarray: 1.5
for n in [[0,0.1],(0,0.1),{0:0.1},{0,0.1},np.array([0,0.1])]:
    for t in Caster.noniterables:
        print(f'From {n} to {typestr(t)}: {cast(n,t)}')
From [0, 0.1] to NoneType: [0, 0.1]
From [0, 0.1] to int: [0, 0]
From [0, 0.1] to float: [0.0, 0.1]
From [0, 0.1] to str: ['0', '0.1']
From (0, 0.1) to NoneType: [0, 0.1]
From (0, 0.1) to int: [0, 0]
From (0, 0.1) to float: [0.0, 0.1]
From (0, 0.1) to str: ['0', '0.1']
From {0: 0.1} to NoneType: {0: 0.1}
From {0: 0.1} to int: {0: 0}
From {0: 0.1} to float: {0: 0.1}
From {0: 0.1} to str: {0: '0.1'}
From {0, 0.1} to NoneType: [0, 0.1]
From {0, 0.1} to int: [0, 0]
From {0, 0.1} to float: [0.0, 0.1]
From {0, 0.1} to str: ['0', '0.1']
From [0.  0.1] to NoneType: [0.0, 0.1]
From [0.  0.1] to int: [0, 0]
From [0.  0.1] to float: [0.0, 0.1]
From [0.  0.1] to str: ['0.0', '0.1']
for n in [[0,0.1],(0,0.1),{0:0.1},{0,0.1},np.array([0,0.1])]:
    for t in Caster.iterables:
        print(f'From {n} to {typestr(t)}: {cast(n,t)}')
From [0, 0.1] to list: [0, 0.1]
From [0, 0.1] to tuple: (0, 0.1)
From [0, 0.1] to dict: {0: 0, 1: 0.1}
From [0, 0.1] to set: {0, 0.1}
From [0, 0.1] to numpy.ndarray: [0.  0.1]
From (0, 0.1) to list: [0, 0.1]
From (0, 0.1) to tuple: (0, 0.1)
From (0, 0.1) to dict: {0: 0, 1: 0.1}
From (0, 0.1) to set: {0, 0.1}
From (0, 0.1) to numpy.ndarray: [0.  0.1]
From {0: 0.1} to list: [(0, 0.1)]
From {0: 0.1} to tuple: ((0, 0.1),)
From {0: 0.1} to dict: {0: 0.1}
From {0: 0.1} to set: {(0, 0.1)}
From {0: 0.1} to numpy.ndarray: dict_items([(0, 0.1)])
From {0, 0.1} to list: [0, 0.1]
From {0, 0.1} to tuple: (0, 0.1)
From {0, 0.1} to dict: {0: 0, 1: 0.1}
From {0, 0.1} to set: {0, 0.1}
From {0, 0.1} to numpy.ndarray: {0, 0.1}
From [0.  0.1] to list: [0.0, 0.1]
From [0.  0.1] to tuple: (0.0, 0.1)
From [0.  0.1] to dict: {0: 0.0, 1: 0.1}
From [0.  0.1] to set: {0.0, 0.1}
From [0.  0.1] to numpy.ndarray: [0.  0.1]

In conclusion, cast can sequentially transition python objects between iterable and noniterable datatypes or any other datatype, with the fallback of using the default python conversions or simply returning the object, and can be updated as a dictionary of callables to include new typecasting behavior.

Data Conversions

Here we present useful data type conversion, like decimal numbers to binary arrays, culminating in the convert function. We also give various helpers such as RNG and analytic continuation of logic such as XOR.

pad[source]

pad(data:Union[ndarray, list], bits:Optional[int]=None, to:Union[int, float]=int)

Pads an array with zeros, up to a length of bits.

pad(data=[1,0],bits=5,to=float)
array([0., 0., 0., 1., 0.])

fill[source]

fill(x:list, fillwith=nan, mask=True)

Turn uneven nested lists x into arrays y substituting missing entries using fillwith and optionally masking.

fill([[1],[1,1,1]],fillwith=0,mask=False)
array([[1, 0, 0],
       [1, 1, 1]])
fill([[1],[1,1,1]],fillwith=0,mask=True)
masked_array(
  data=[[1, --, --],
        [1, 1, 1]],
  mask=[[False,  True,  True],
        [False, False, False]],
  fill_value=0)

nbits[source]

nbits(x:Union[int, float, list, ndarray])

Return the number of bits required to represent the input x.

for i in range(11):
    print(f"i={i}, nbits({i})={nbits(i)}")
i=0, nbits(0)=1
i=1, nbits(1)=1
i=2, nbits(2)=2
i=3, nbits(3)=2
i=4, nbits(4)=3
i=5, nbits(5)=3
i=6, nbits(6)=3
i=7, nbits(7)=3
i=8, nbits(8)=4
i=9, nbits(9)=4
i=10, nbits(10)=4

num2ar[source]

num2ar(x:Union[int, float], bits:Optional[int]=None, to:Union[int, float]=int)

Converts decimal number x to array a zero-padded with bits.

for i in range(11):
    print(f"i={i}, num2ar({i})={num2ar(i)}")
i=0, num2ar(0)=[0]
i=1, num2ar(1)=[1]
i=2, num2ar(2)=[1, 0]
i=3, num2ar(3)=[1, 1]
i=4, num2ar(4)=[1, 0, 0]
i=5, num2ar(5)=[1, 0, 1]
i=6, num2ar(6)=[1, 1, 0]
i=7, num2ar(7)=[1, 1, 1]
i=8, num2ar(8)=[1, 0, 0, 0]
i=9, num2ar(9)=[1, 0, 0, 1]
i=10, num2ar(10)=[1, 0, 1, 0]
num2ar(10,bits=5,to=float)
[0.0, 1.0, 0.0, 1.0, 0.0]

ar2num[source]

ar2num(a:Union[list, ndarray], to:Union[int, float]=int)

Converts array a to decimal number x.

for i in range(4):
    print(f"a={num2ar(i)}, ar2num(a)={ar2num(num2ar(i))}")
a=[0], ar2num(a)=0
a=[1], ar2num(a)=1
a=[1, 0], ar2num(a)=2
a=[1, 1], ar2num(a)=3

ar2hex[source]

ar2hex(a:Union[list, ndarray], bits:Optional[int]=None, prefix:bool=True)

Converts binary array to hex string in: a (numpy array) : binary array to convert out: h (str) : hex conversion of a

ar2hex(num2ar(10))
'0x0a'
ar2hex(num2ar(10),prefix=False)
'0a'

hex2ar[source]

hex2ar(h:str, bits:Optional[int]=None, to:Union[int, float]=int)

Converts a hex string h into an array a padded with bits and elements of astype.

hex2ar('a')
[1, 0, 1, 0]
hex2ar('0xa')
[1, 0, 1, 0]

str2ar[source]

str2ar(s:str, to:Union[list, ndarray]=ndarray)

Converts an input string s into an array or list a as per astype.

str2ar('111')
array([1, 1, 1])

ar2str[source]

ar2str(a:Union[list, ndarray], to:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray]=None)

Converts an input array a into a string s.

ar2str([1.5,1.5,1.3],int)
'111'

COPY[source]

COPY(x:Union[int, float])

Simply returns x.

Exclusive_OR[source]

Exclusive_OR(x:Union[int, float], y:Union[int, float])

Return logical exclusive OR of x and y. See DeMorgan's Laws.

NOT[source]

NOT(x:Union[int, float])

Return conjugate of x.

NOT(1)
0
NOT(0.25)
0.75

AND[source]

AND(x:Union[int, float], y:Union[int, float])

Return logical AND of x and y.

AND(1,1)
1
AND(1,0.5)
0.5

OR[source]

OR(x:Union[int, float], y:Union[int, float])

Return logical OR of x and y. See DeMorgan's Laws.

OR(1,0)
1
OR(0.5,0.5)
0.75

XOR[source]

XOR(*args:Union[int, float, list, ndarray])

Arbitrary input XOR using recursiveness.

XOR(1,1)
0
XOR(1,1,1)
1
XOR(0.5,0.5,0.5)
0.4384765625
for x,y in np.ndindex((2,2)):
    for z in [AND,OR,XOR]:
        print(f"{z.__name__}{x,y}={z(x,y)}")
AND(0, 0)=0
OR(0, 0)=0
XOR(0, 0)=0
AND(0, 1)=0
OR(0, 1)=1
XOR(0, 1)=1
AND(1, 0)=0
OR(1, 0)=1
XOR(1, 0)=1
AND(1, 1)=1
OR(1, 1)=1
XOR(1, 1)=0

ar2gr[source]

ar2gr(binary:Union[list, ndarray], to:Union[list, ndarray]=ndarray)

Converts an input binary array to graycode.

ar2gr(num2ar(10))
array([1, 1, 1, 1])

gr2ar[source]

gr2ar(gray:Union[list, ndarray], to:Union[list, ndarray]=ndarray)

Converts a gray-code array into binary.

gr2ar(ar2gr(num2ar(10)))
array([1, 0, 1, 0])

num2gr[source]

num2gr(x:int, to:Union[NoneType, int, float, list, tuple, str, dict, set, ndarray]=None)

Converts decimal number x to equivalent gray-code number.

for i in range(11):
    print(f"i={i}, num2gr(i)={num2gr(i)}")
i=0, num2gr(i)=0
i=1, num2gr(i)=1
i=2, num2gr(i)=3
i=3, num2gr(i)=2
i=4, num2gr(i)=6
i=5, num2gr(i)=7
i=6, num2gr(i)=5
i=7, num2gr(i)=4
i=8, num2gr(i)=12
i=9, num2gr(i)=13
i=10, num2gr(i)=15

convert[source]

convert(obj:Union[int, float, list, hex, str, ndarray], to:Union[int, float, list, hex, str, ndarray]=ndarray, bits:Optional[int]=None, astype:Union[int, float, list, ndarray]=int, gray:bool=False)

Converts an input obj into an output of type to, padding with bits. Internally converts obj to np.ndarray with elements of dtype astype, before converting to the desired dtype to. If gray, first converts this binary array to gray-code. If input or output are hex, requires prefix of 0x.

Possible conversions: int -> float int -> str int -> list int -> array int -> hex

str -> int
str -> float
str -> list
str -> array
str -> hex

list -> arr
list -> int
list -> float
list -> str
list -> hex

arr -> list
arr -> int
arr -> float
arr -> str
arr -> hex

hex -> int
hex -> float
hex -> arr
hex -> list
hex -> str
for i in [10,10.0,'10','0xa']:
    t_in=str(type(i)).split('<')[-1].split('>')[0].split('class')[-1].split('\'')[1]
    for j in [int,float,list,str,hex]:#,set,dict]:
        t_out='hex' if j==hex else str(j).split('<')[-1].split('>')[0].split('class')[-1].split('\'')[1]
        print(f'From {t_in} to {t_out}: convert({i},{t_out})={convert(i,j)}')
From int to int: convert(10,int)=10
From int to float: convert(10,float)=10.0
From int to list: convert(10,list)=[1, 0, 1, 0]
From int to str: convert(10,str)=1010
From int to hex: convert(10,hex)=0xa
From float to int: convert(10.0,int)=10
From float to float: convert(10.0,float)=10.0
From float to list: convert(10.0,list)=[1, 0, 1, 0]
From float to str: convert(10.0,str)=1010
From float to hex: convert(10.0,hex)=0xa
From str to int: convert(10,int)=2
From str to float: convert(10,float)=2.0
From str to list: convert(10,list)=[1, 0]
From str to str: convert(10,str)=10
From str to hex: convert(10,hex)=0x2
From str to int: convert(0xa,int)=10
From str to float: convert(0xa,float)=10.0
From str to list: convert(0xa,list)=[1, 0, 1, 0]
From str to str: convert(0xa,str)=1010
From str to hex: convert(0xa,hex)=0xa
for i in [10,10.0,'10','0xa']:#,{0,10},{0:10,'a':11}]:
    t_in=str(type(i)).split('<')[-1].split('>')[0].split('class')[-1].split('\'')[1]
    for j in [int,float,list,str,hex]:#,set,dict]:
        t_out='hex' if j==hex else str(j).split('<')[-1].split('>')[0].split('class')[-1].split('\'')[1]
        print(f'Gray Code: From {t_in} to {t_out}: convert({i},{t_out})={convert(i,j,gray=True)}')
Gray Code: From int to int: convert(10,int)=15
Gray Code: From int to float: convert(10,float)=15.0
Gray Code: From int to list: convert(10,list)=[1, 1, 1, 1]
Gray Code: From int to str: convert(10,str)=1111
Gray Code: From int to hex: convert(10,hex)=0xf
Gray Code: From float to int: convert(10.0,int)=15
Gray Code: From float to float: convert(10.0,float)=15.0
Gray Code: From float to list: convert(10.0,list)=[1, 1, 1, 1]
Gray Code: From float to str: convert(10.0,str)=1111
Gray Code: From float to hex: convert(10.0,hex)=0xf
Gray Code: From str to int: convert(10,int)=3
Gray Code: From str to float: convert(10,float)=3.0
Gray Code: From str to list: convert(10,list)=[1, 1]
Gray Code: From str to str: convert(10,str)=11
Gray Code: From str to hex: convert(10,hex)=0x3
Gray Code: From str to int: convert(0xa,int)=15
Gray Code: From str to float: convert(0xa,float)=15.0
Gray Code: From str to list: convert(0xa,list)=[1, 1, 1, 1]
Gray Code: From str to str: convert(0xa,str)=1111
Gray Code: From str to hex: convert(0xa,hex)=0xf

rint[source]

rint(x:Union[int, float, list, ndarray])

Typecast rounding to np arrays.

rint(np.array([0.5,0.51]))
array([0, 1])