Based on this StackOverflow answer:
NumPy does not support jagged arrays natively. gives an array that may or may not behave as you expect.
A workaround using masked arrays can be as follows:
import numpy as np
import numpy.ma as ma
a = np.array([0, 1])
b = np.array([2, 3, 4, 5])
c = np.array([6, 7, 8, 9, 10, 11])
jagged_array = ma.vstack(
[
ma.array(np.resize(a, c.shape[0]), mask=[False, False, True, True, True, True]),
ma.array(
np.resize(b, c.shape[0]), mask=[False, False, False, False, True, True]
),
c,
]
)
print(jagged_array)
print(jagged_array.ndim)
print(jagged_array.shape)
Your output would look like:
❯ python3 sample.py
[[0 1 -- -- -- --]
[2 3 4 5 -- --]
[6 7 8 9 10 11]]
2
(3, 6)
Answer from user4109800 on Stack OverflowBased on this StackOverflow answer:
NumPy does not support jagged arrays natively. gives an array that may or may not behave as you expect.
A workaround using masked arrays can be as follows:
import numpy as np
import numpy.ma as ma
a = np.array([0, 1])
b = np.array([2, 3, 4, 5])
c = np.array([6, 7, 8, 9, 10, 11])
jagged_array = ma.vstack(
[
ma.array(np.resize(a, c.shape[0]), mask=[False, False, True, True, True, True]),
ma.array(
np.resize(b, c.shape[0]), mask=[False, False, False, False, True, True]
),
c,
]
)
print(jagged_array)
print(jagged_array.ndim)
print(jagged_array.shape)
Your output would look like:
❯ python3 sample.py
[[0 1 -- -- -- --]
[2 3 4 5 -- --]
[6 7 8 9 10 11]]
2
(3, 6)
def ndim(arr):
return len(arr)-1
jagged_array = np.array([[None, None], [None, None, None, None], [None, None, None,None, None, None]])
print(jagged_array)
print(ndim(jagged_array))
print(jagged_array.shape)
What is the Python equivalent of a jagged array? - Stack Overflow
python jagged array operation efficiency - Stack Overflow
How to make a jagged array neat in Python? - Stack Overflow
python - Convert jagged lists into numpy array - Stack Overflow
Your array is 2x2:
In [298]: A
Out[298]:
array([[array([1, 2, 3]), array([4, 5])],
[array([6, 7, 8, 9]), array([10])]], dtype=object)
While A+A works, boolean tests have not been implemented for this kind of array:
In [299]: A>4
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I'm going to flatten A because it makes it easier to compare with list operations:
In [301]: A1=A.flatten()
In [303]: A1+A1
Out[303]:
array([array([2, 4, 6]), array([ 8, 10]), array([12, 14, 16, 18]),
array([20])], dtype=object)
In [304]: [a+a for a in A1]
Out[304]: [array([2, 4, 6]), array([ 8, 10]), array([12, 14, 16, 18]), array([20])]
In [305]: timeit A1+A1
100000 loops, best of 3: 6.85 µs per loop
In [306]: timeit [a+a for a in A1]
100000 loops, best of 3: 9.09 µs per loop
The array operation is a bit faster than a list comprehension. But if I first turn the array into a list:
In [307]: A1l=A1.tolist()
In [308]: A1l
Out[308]: [array([1, 2, 3]), array([4, 5]), array([6, 7, 8, 9]), array([10])]
In [309]: timeit [a+a for a in A1l]
100000 loops, best of 3: 5.2 µs per loop
times improve. This is a good indication that the A1+A1 (or even A+A) is using a similar sort of iteration.
So the straight forward way of performing your A,B calculation is
In [310]: A2=[a[a>4] for a in A1]
In [311]: B=[a+a for a in A2]
In [312]: B
Out[312]: [array([], dtype=int32), array([10]), array([12, 14, 16, 18]), array([20])]
(we can convert to/from arrays and lists as needed).
A numpy array stores its data a flat databuffer, and uses the shape and strides attributes to quickly calculate the location of any element, regardless of the dimensions. The fast array operations use compiled code that rapidly steps though the databuffers of arguments, performing the operations element by element (or some other combination).
A dtype object array also has the flat databuffer, but the elements are pointers to lists or arrays elsewhere. So while it can index individual elements quickly, it still has to perform a Python call(s) to access the arrays. So especially when the array is 1d, it is virtually the same as a flat list with the same pointers.
Multidimensional object arrays are nicer than nested lists. You can reshape them, access elements (A[1,3] v Al[1][3]), transpose them, etc. But when it comes to iterating through all the subarrays they don't offer much of a benefit.
Looking again at your 2d array:
In [315]: timeit A+A
100000 loops, best of 3: 6.93 µs per loop # 6.85 for A1+A1 (above)
In [316]: timeit [[j+j for j in i] for i in A]
100000 loops, best of 3: 17.1 µs per loop
In [317]: Al = A.tolist()
In [318]: timeit [[j+j for j in i] for i in Al]
100000 loops, best of 3: 7.01 µs per loop # 5.2 for A1l flat list
Basically the same time for summing the array and iterating through the equivalent nested list.
The performance of numpy jagged array may not be optimal, but there are enough reasons to believe that it should be much better than using python nested list. As explained in your earlier post:
On principle you should have some performance bonus because every element is a numpy array. So you just need a 2 dimensional loop rather than a 3D loop (if you store every number in nested lists). Also it always saves you lots of memory allocation time to avoid using python list.
Here is a simple test:
import time,sys,random
import numpy as np
rand = np.random.rand
L = np.array([[rand(100), rand(200)],[rand(400), rand(300)]], dtype=object)
L1 = [random.random() for i in range(1000)]
arrFunc = np.vectorize(lambda x:x[x>0.3],otypes=[np.ndarray])
start = time.time()
if sys.argv[1]=='np':
for i in range(100000):
B=i*L
else:
for i in range(100000):
B=[i*x for x in L1]
end = time.time()
print ('Arithmetic Op: ', end-start)
start = time.time()
if sys.argv[1]=='np':
for i in range(100000):
B=arrFunc(L)
else:
for i in range(100000):
B=[x for x in L1 if x<0.3]
end = time.time()
print ('Indexing ', end-start)
Result:
> python testNpJarray.py np
Arithmetic Op: 3.9719998836517334
Indexing 8.079999923706055
> python testNpJarray.py list
Arithmetic Op: 53.289000034332275
Indexing 52.10899996757507
This test may not be quite fare because the outter numpy array is quite small, you are welcome to change the size to fit into your application and tell us the results.
Unless I misunderstand the question, you just want the product of the sub-lists, although you have to wrap any single elements into lists first.
>>> from itertools import product
>>> arr = ['a', ['e', 'r', 't'], ['c', 'd']]
>>> listified = [x if isinstance(x, list) else [x] for x in arr]
>>> listified
[['a'], ['e', 'r', 't'], ['c', 'd']]
>>> list(product(*listified))
[('a', 'e', 'c'),
('a', 'e', 'd'),
('a', 'r', 'c'),
('a', 'r', 'd'),
('a', 't', 'c'),
('a', 't', 'd')]
I have a recursive solution:
inlist1 = ['ab', ['e', 'r', 't'], ['c', 'd']]
inlist2 = [['a', 'b'], ['e', 'r', 't'], ['c', 'd']]
inlist3 = [['a', 'b'], 'e', ['c', 'd']]
def jagged(inlist):
a = [None] * len(inlist)
def _jagged(index):
if index == 0:
print(a)
return
v = inlist[index - 1]
if isinstance(v, list):
for i in v:
a[index - 1] = i
_jagged(index - 1, )
else:
a[index - 1] = v
_jagged(index - 1)
_jagged(len(inlist))
jagged(inlist3)
What about using np.vectorize:
do_avg = np.vectorize(np.average)
data_2d = do_avg(data)
data = np.array([[1,2,3],[0,3,2,4],[0,2],[1]]).reshape(2,2)
avg=np.zeros(data.shape)
avg.flat=[np.average(x) for x in data.flat]
print avg
#array([[ 2. , 2.25],
# [ 1. , 1. ]])
This still iterates over the elements of data (nothing un-Pythonic about that). But since there's nothing special about the shape or axes of data, I'm just using data.flat. While appending to Python list, with numpy it is better to assign values to the elements of an existing array.
There are fast numeric methods to work with numpy arrays, but most (if not all) work with simple numeric dtypes. Here the array elements are object (either list or array), numpy has to resort to the usual Python iteration and list operations.
For this small example, this solution is a bit faster than Zwicker's vectorize. For larger data the two solutions take about the same time.
Short answer: you can't. NumPy does not support jagged arrays natively.
Long answer:
>>> a = ones((3,))
>>> b = ones((2,))
>>> c = array([a, b])
>>> c
array([[ 1. 1. 1.], [ 1. 1.]], dtype=object)
gives an array that may or may not behave as you expect. E.g. it doesn't support basic methods like sum or reshape, and you should treat this much as you'd treat the ordinary Python list [a, b] (iterate over it to perform operations instead of using vectorized idioms).
Several possible workarounds exist; the easiest is to coerce a and b to a common length, perhaps using masked arrays or NaN to signal that some indices are invalid in some rows. E.g. here's b as a masked array:
>>> ma.array(np.resize(b, a.shape[0]), mask=[False, False, True])
masked_array(data = [1.0 1.0 --],
mask = [False False True],
fill_value = 1e+20)
This can be stacked with a as follows:
>>> ma.vstack([a, ma.array(np.resize(b, a.shape[0]), mask=[False, False, True])])
masked_array(data =
[[1.0 1.0 1.0]
[1.0 1.0 --]],
mask =
[[False False False]
[False False True]],
fill_value = 1e+20)
(For some purposes, scipy.sparse may also be interesting.)
In general, there is an ambiguity in putting together arrays of different length because alignment of data might matter. Pandas has different advanced solutions to deal with that, e.g. to merge series into dataFrames.
If you just want to populate columns starting from first element, what I usually do is build a matrix and populate columns. Of course you need to fill the empty spaces in the matrix with a null value (in this case np.nan)
a = ones((3,))
b = ones((2,))
arraylist=[a,b]
outarr=np.ones((np.max([len(ps) for ps in arraylist]),len(arraylist)))*np.nan #define empty array
for i,c in enumerate(arraylist): #populate columns
outarr[:len(c),i]=c
In [108]: outarr
Out[108]:
array([[ 1., 1.],
[ 1., 1.],
[ 1., nan]])