Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Better way to pack numpy array?

Tags:

python

numpy

I need to pack a numpy 2D array with struct.pack, and I'm seeking a method which can do this in a batch. I tried:

X = numpy.array([[1,2,3],[4,5,6]])
b = struct.pack('=%sf' % X.size, *X)

but this doesn't work. It prompts:

struct.error: pack expected 6 items for packing (got 2)

Is there a better way to pack the NumPy array instead of looping over each element?

like image 568
Jedi Avatar asked Jul 09 '15 02:07

Jedi


People also ask

Is appending to NumPy array faster than list?

array(a) . List append is faster than array append .

Is Ndarray in NumPy better than a list?

Numpy data structures perform better in: Size - Numpy data structures take up less space. Performance - they have a need for speed and are faster than lists. Functionality - SciPy and NumPy have optimized functions such as linear algebra operations built in.

Is NumPy concatenate faster than append?

In general it is better/faster to iterate or append with lists, and apply the np. array (or concatenate) just once. appending to a list is fast; much faster than making a new array.

Does ZIP work on NumPy arrays?

If we want to bind or zip the data of different array then we can go for zip function in python of NumPy. This function eliminates the need of class object if not required. We can easily map our data with any number of the array and this can be done very easily with the use of the zip() function.


3 Answers

If you have a numpy array, you already have your data packed in memory, you don't need to use struct:

>>> a = np.arange(1, 7)
>>> struct.pack('=6f', *a)
'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x00\x00\xa0@\x00\x00\xc0@'

>>> a.astype('f').tostring()
'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x00\x00\xa0@\x00\x00\xc0@'

And if your array is multidimensional, .tostring takes a flattened view by default:

>>> a = np.arange(1, 7).reshape(2, 3)
>>> a.astype('f').tostring()
'\x00\x00\x80?\x00\x00\x00@\x00\x00@@\x00\x00\x80@\x00\x00\xa0@\x00\x00\xc0@'
like image 63
Jaime Avatar answered Oct 15 '22 12:10

Jaime


Seemed the '*' operator combined with struct.pack only works with 1D array. So for ND array, it has to be flattened to 1D firstly.

X = numpy.array([[1,2,3],[4,5,6]])
b = struct.pack('=%sf' % X.size, *X.flatten('F'))

This works for me.

like image 31
Jedi Avatar answered Oct 15 '22 10:10

Jedi


The * acts like an iterator. Iterating on an 2d array returns the rows of that array. For example:

In [9]: def foo(*args):
    print(args)    
In [10]: foo(*np.ones((2,3)))
(array([ 1.,  1.,  1.]), array([ 1.,  1.,  1.]))

args, inside the function, is 2 tuples, each a row of the array. That is why your pack got 2 items, rather than 6 (x.size). When you flatten the array, * produces all 6 items.

In [11]: foo(*np.ones((2,3)).flat)
(1.0, 1.0, 1.0, 1.0, 1.0, 1.0)

But what's the purpose of this pack?

like image 30
hpaulj Avatar answered Oct 15 '22 11:10

hpaulj