53

I'm having a little trouble here,

I'm trying to convert a numpy.ndarray to string, I've already done that like this:

randomArray.tostring()

It works, but I'm wondering if I can transform it back to a numpy.ndarray.

What's the best way to do this?

I'm using numpy 1.8.1

Context: The objective is to send the numpy.ndarray as a message in rabbitmq (pika library)

Ampo
  • 533
  • 1
  • 4
  • 6
  • You might find this answer useful: [1]: http://stackoverflow.com/questions/5387208/convert-a-string-to-an-array – Singularity May 11 '15 at 12:30
  • Sadly the tostring() method returns bytes and I don't know how to convert it even with this solution. – Ampo May 11 '15 at 12:33
  • Note that `.tostring()` [is deprecated in NumPy 1.19](https://github.com/numpy/numpy/pull/15867), with the preferred spelling being `.tobytes()`. The two otherwise have identical behavior. – Eric Apr 01 '20 at 09:33

9 Answers9

53

You can use the fromstring() method for this:

arr = np.array([1, 2, 3, 4, 5, 6])
ts = arr.tostring()
print(np.fromstring(ts, dtype=int))

>>> [1 2 3 4 5 6]

Sorry for the short answer, not enough points for commenting. Remember to state the data types or you'll end up in a world of pain.

Note on fromstring from numpy 1.14 onwards:

sep : str, optional

The string separating numbers in the data; extra whitespace between elements is also ignored.

Deprecated since version 1.14: Passing sep='', the default, is deprecated since it will trigger the deprecated binary mode of this function. This mode interprets string as binary bytes, rather than ASCII text with decimal numbers, an operation which is better spelt frombuffer(string, dtype, count). If string contains unicode text, the binary mode of fromstring will first encode it into bytes using either utf-8 (python 3) or the default encoding (python 2), neither of which produce sane results.

Augustin
  • 2,444
  • 23
  • 24
ajsp
  • 2,512
  • 22
  • 34
  • 3
    i did not know about `fromstring`, nice ! however, it does not seem to work multi-dimensional arrays (returns a `flat` version of the multi-dimensional array). I guess you can reshape the array afterwards if you know the dimensions. – Julien Spronck May 11 '15 at 12:54
  • 1
    This may work, the weird thing is that my `tostring()` method returns weird things (bytes?) the `fromstring()` isn't working perfectly. – Ampo May 11 '15 at 13:11
  • @Ampo you can use repr(ts) to view the binary, but you will have to convert it using np.fromstring(ts,dtype=int), remember to use the correct data type. Are you using floats or integers? Post the type of array you are trying to send. – ajsp May 11 '15 at 13:16
  • I'm using integers. The exact type of the first array is numpy.uint8 – Ampo May 11 '15 at 13:22
  • Then use np.fromstring(ts,np.uint). Tell me about the result. – ajsp May 11 '15 at 13:28
  • 1
    Frankly I would not serialize with numpy, my advice is to dump the lot into JSON and parse it at the other end...no headaches. – ajsp May 11 '15 at 13:36
  • 1
    `np.fromstring()` is depricated, use `np.frombuffer()` instead – Scott Sep 06 '19 at 18:02
  • @Sherzod It is not deprecated - still in the latest version, 1.17.1. – ajsp Sep 08 '19 at 01:20
  • @ajsp: Yes it is, and has been since 1.14. Source: [I deprecated it](https://github.com/numpy/numpy/pull/9487). `tostring()` is also deprecated since 1.19. – Eric Apr 01 '20 at 09:37
  • @Eric https://docs.scipy.org/doc/numpy/reference/generated/numpy.fromstring.html – ajsp Apr 01 '20 at 17:21
  • What's your point? From your link, the docs contain _"Deprecated since version 1.14"_. Sure, it's only the binary mode that's deprecated, but that's the mode this answer uses. – Eric Apr 02 '20 at 01:32
28

If you use tostring you lose information on both shape and data type:

>>> import numpy as np
>>> a = np.arange(12).reshape(3, 4)
>>> a
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
>>> s = a.tostring()
>>> aa = np.fromstring(a)
>>> aa
array([  0.00000000e+000,   4.94065646e-324,   9.88131292e-324,
         1.48219694e-323,   1.97626258e-323,   2.47032823e-323,
         2.96439388e-323,   3.45845952e-323,   3.95252517e-323,
         4.44659081e-323,   4.94065646e-323,   5.43472210e-323])
>>> aa = np.fromstring(a, dtype=int)
>>> aa
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
>>> aa = np.fromstring(a, dtype=int).reshape(3, 4)
>>> aa
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

This means you have to send the metadata along with the data to the recipient. To exchange auto-consistent objects, try cPickle:

>>> import cPickle
>>> s = cPickle.dumps(a)
>>> cPickle.loads(s)
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])
simleo
  • 2,775
  • 22
  • 23
13

Imagine you have a numpy array of integers (it works with other types but you need some slight modification). You can do this:

a = np.array([0, 3, 5])
a_str = ','.join(str(x) for x in a) # '0,3,5'
a2 = np.array([int(x) for x in a_str.split(',')]) # np.array([0, 3, 5])

If you have an array of float, be sure to replace int by float in the last line.

You can also use the __repr__() method, which will have the advantage to work for multi-dimensional arrays:

from numpy import array
numpy.set_printoptions(threshold=numpy.nan)
a = array([[0,3,5],[2,3,4]])
a_str = a.__repr__() # 'array([[0, 3, 5],\n       [2, 3, 4]])'
a2 = eval(a_str) # array([[0, 3, 5],
                 #        [2, 3, 4]])
Julien Spronck
  • 15,069
  • 4
  • 47
  • 55
  • Since I use a 3D-Array (image) the `__repr__()` method should work but it doesn't. The array is really big (1000000+ values in it) I end up with 1000 values after converting it with `__repr__()` and `eval()` crashes(?) – Ampo May 11 '15 at 13:16
  • @Ampo yes, __repr__() crashes with larger arrays because of the representation of large numpy arrays (large arrays have `...` instead of full arrays). You can change that behaviour (with set_printoptions) ... I just edited my answer, see if that works better. – Julien Spronck May 11 '15 at 13:31
  • It may be helpful to add import numpy to your second code, since it gives an error (numpy.set_printoptions) for those who don't know. – Anurag A S Jan 20 '19 at 06:35
4

I know, I am late but here is the correct way of doing it. using base64. This technique will convert the array to string.

import base64
import numpy as np
random_array = np.random.randn(32,32)
string_repr = base64.binascii.b2a_base64(random_array).decode("ascii")
array = np.frombuffer(base64.binascii.a2b_base64(string_repr.encode("ascii"))) 
array = array.reshape(32,32)

For array to string

Convert binary data to a line of ASCII characters in base64 coding and decode to ASCII to get string repr.

For string to array

First, encode the string in ASCII format then Convert a block of base64 data back to binary and return the binary data.

shantanu pathak
  • 2,018
  • 19
  • 26
aman5319
  • 662
  • 5
  • 16
2

This is a fast way to encode the array, the array shape and the array dtype:

def numpy_to_bytes(arr: np.array) -> str:
    arr_dtype = bytearray(str(arr.dtype), 'utf-8')
    arr_shape = bytearray(','.join([str(a) for a in arr.shape]), 'utf-8')
    sep = bytearray('|', 'utf-8')
    arr_bytes = arr.ravel().tobytes()
    to_return = arr_dtype + sep + arr_shape + sep + arr_bytes
    return to_return

def bytes_to_numpy(serialized_arr: str) -> np.array:
    sep = '|'.encode('utf-8')
    i_0 = serialized_arr.find(sep)
    i_1 = serialized_arr.find(sep, i_0 + 1)
    arr_dtype = serialized_arr[:i_0].decode('utf-8')
    arr_shape = tuple([int(a) for a in serialized_arr[i_0 + 1:i_1].decode('utf-8').split(',')])
    arr_str = serialized_arr[i_1 + 1:]
    arr = np.frombuffer(arr_str, dtype = arr_dtype).reshape(arr_shape)
    return arr

To use the functions:

a = np.ones((23, 23), dtype = 'int')
a_b = numpy_to_bytes(a)
a1 = bytes_to_numpy(a_b)
np.array_equal(a, a1) and a.shape == a1.shape and a.dtype == a1.dtype
Jadiel de Armas
  • 8,405
  • 7
  • 46
  • 62
  • Thanks for the solution. Just a small fix: In numpy_to_bytes the output type should be "bytearray" and the bytes_to_numpy input type should be "bytearray" as well. – Aref Mar 31 '21 at 14:17
1

This is a slightly improvised answer to ajsp answer using XML-RPC.

On the server-side when you convert the data, convert the numpy data to a string using the '.tostring()' method. This encodes the numpy ndarray as bytes string. On the client-side when you receive the data decode it using '.fromstring()' method. I wrote two simple functions for this. Hope this is helpful.

  1. ndarray2str -- Converts numpy ndarray to bytes string.
  2. str2ndarray -- Converts binary str back to numpy ndarray.
    def ndarray2str(a):
        # Convert the numpy array to string 
        a = a.tostring()

        return a

On the receiver side, the data is received as a 'xmlrpc.client.Binary' object. You need to access the data using '.data'.

    def str2ndarray(a):
        # Specify your data type, mine is numpy float64 type, so I am specifying it as np.float64
        a = np.fromstring(a.data, dtype=np.float64)
        a = np.reshape(a, new_shape)

        return a

Note: Only problem with this approach is that XML-RPC is very slow while sending large numpy arrays. It took me around 4 secs to send and receive a (10, 500, 500, 3) size numpy array for me.

I am using python 3.7.4.

Sudheer Raja
  • 151
  • 1
  • 8
1

The right answer for for numpy version >1.9

arr = np.array([1, 2, 3, 4, 5, 6])
ts = arr.tobytes()
#Reverse to array
arr = np.frombuffer(ts, dtype=arr.dtype)
print(arr)

tostring() is deprecated

You don't need any external library (except numpy) and its there is no faster method to retrive the value!

SachaDee
  • 9,245
  • 3
  • 23
  • 33
0

I needed it to save the ndarray in an SQLite table.

My solution was to dump the array and convert it into hexadecimal:

array_5_str = array_5.dumps().hex()  # to save it in the table

To convert it into a ndarray again:

array_5_from_str = pickle.loads(bytes.fromhex(array_5_str))

You can compare the two ndarray with:

comparison = array_5 == array_5_from_str
equal_arrays = comparison.all()
print(equal_arrays)
heilala
  • 770
  • 8
  • 19
Leo Mag
  • 1
  • 1
  • 1
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – xlmaster Mar 03 '23 at 13:02
-4

Imagine you have a numpy array of text like in a messenger

 >>> stex[40]
 array(['Know the famous thing ...

and you want to get statistics from the corpus (text col=11) you first must get the values from dataframe (df5) and then join all records together in one single corpus:

 >>> stex = (df5.ix[0:,[11]]).values
 >>> a_str = ','.join(str(x) for x in stex)
 >>> a_str = a_str.split()
 >>> fd2 = nltk.FreqDist(a_str)
 >>> fd2.most_common(50)
Max Kleiner
  • 1,442
  • 1
  • 13
  • 14