0

Is it possible to assign a single numpy object to several variables in one go?

For instance, I have many variables that I'd like to initiate with np.zeros((2,), dtype=np.object)

I thought of doing a,b,c = np.zeros((2,), dtype=np.object) but got a ValueError. a=b=c=np.zeros((2,), dtype=np.object) doesn't work either because if I change one variable the others also change.

With three variables like the example above it isn't a problem to do it manually but when I have dozens it would be convenient to be able to do this in a more effient way.

EDIT - A more concrete example would be:

a = np.zeros((2,),dtype=np.object)
b = np.zeros((2,),dtype=np.object)
c = np.zeros((2,),dtype=np.object)


a[0] = pd.Series(data=np.random.rand(10))
a[1] = pd.Series(data=np.random.rand(5))

b[0] = 'text1'
b[1] = 'text2'

c[0] = [0,0,0,0,0,0,0,0,0]
c[1] = [1,1,1,1,1]

data = {'a':a, 'b':b, 'c':c}

sio.savemat('test.mat',{'data':data})
12
  • 1
    Why do you have dozens of identically-initialized variables in the same place? Commented Nov 21, 2013 at 19:52
  • 1
    If you want to "assign a single numpy object to several variables", then you want changes in one variable to show up in all, because they are all the same object. What you seem to want is to set several variables not to a single object, but to several equal objects. But if you have dozens of variables all being initialized the same way, you probably want a list or dictionary instead. Commented Nov 21, 2013 at 19:52
  • Any time you have dozens of variables, most likely you don't actually want dozens of variables, but a single list or dict with dozens of members. Commented Nov 21, 2013 at 19:52
  • 1
    @HappyPy: What does "exported to a different program" mean? Almost all ways that could be interpreted sensibly will end up giving the other program a copy of the array, not the object itself, in which case this whole point is moot. Commented Nov 21, 2013 at 19:54
  • 2
    @HappyPy: You can have a list of NumPy arrays. (Or, maybe even better, you can just add another dimension onto your array instead of having a bunch of separate arrays. Or do something similar with Pandas.) Commented Nov 21, 2013 at 19:55

4 Answers 4

5

Use a list comprehension:

>>> a, b, c = [np.zeros((2,), dtype=np.object) for _ in xrange(3)]
>>> [id(x) for x in (a,b,c)]      
[172542064, 171775688, 168893512] #Different objects

If you're planning to add dozens of variables like this, then better use a dictionary:

>>> my_vars = {x:np.zeros((2,), dtype=np.object) for x in 'abcdef'}
>>> my_vars
{'a': array([0, 0], dtype=object), 'c': array([0, 0], dtype=object), 'b': array([0, 0], dtype=object), 'e': array([0, 0], dtype=object), 'd': array([0, 0], dtype=object), 'f': array([0, 0], dtype=object)}
Sign up to request clarification or add additional context in comments.

2 Comments

First line should just be: a, b, c = np.zeros((3,2), dtype=np.object)
@askewchan: Thanks, that's actually what I was looking for. Would you like to post your comment as an answer so that I can mark it as accepted?
1

A nice way to make several arrays is to add the number of desired output arrays as the first dimension (axis 0). So, if you want three arrays of length two, filled with zeros of dtype object, you can do this:

a, b, c = np.zeros((3,2), object)

This works for any array, where the unpacking is from the first axis, for some examples:

a, b, c = np.random.rand(3, 4, 5)
a, b  = np.arange(12).reshape(2,-1)

N.B.: This is not identical to doing

a, b, c = [np.zeros((2,), object) for _ in xrange(3)]

Because in that case each of the arrays were created separately, and won't necessarily be contiguous in memory. If you use the unpacking of a single array, the original array was first created as one single contiguous array, and the new arrays are just views of the original array. You haven't saved the original array, so I can't see this in any effect, but if you did store, then split the array, you'd see they shared data:

In [96]: orig = np.zeros((3,2), dtype=np.object)

In [97]: a, b, c = orig

In [98]: orig
Out[98]: 
array([[0, 0],
       [0, 0],
       [0, 0]], dtype=object)

In [99]: a
Out[99]: array([0, 0], dtype=object)

In [100]: a[0] = 9

In [101]: a
Out[101]: array([9, 0], dtype=object)

In [102]: orig
Out[102]: 
array([[9, 0],
       [0, 0],
       [0, 0]], dtype=object)

But without the orig array being saved/referred to elsewhere, I can't see this being an issue. The only way to see the difference is by checking the flags:

In [103]: a, b, c = np.zeros((3,2), dtype=np.object)

In [104]: a.flags.owndata
Out[104]: False

In [105]: a, b, c = [np.zeros((2,), dtype=np.object) for _ in xrange(3)]

In [106]: a.flags.owndata
Out[106]: True

Comments

1

In your actual code, there is no reason to have these variables just so you can insert them all into a dict. And there's also no reason to initialize the values to equal arrays in the first place just so you can then mutate them into the arrays you actually want. If you write everything in the obvious way, the problem you're trying to solve never even arises:

data = {}
data['a'] = np.array([pd.Series(data=np.random.rand(10)),
                      pd.Series(data=np.random.rand(5))])
data['b'] = np.array(['text1', 'text2'])
data['c'] = np.array([[0,0,0,0,0,0,0,0,0], [1,1,1,1,1]])
sio.savemat('test.mat',{'data':data})

If you think you need to pre-create and then mutate the arrays to guarantee that you get the results you want, as you claim in the comments… well, first, just run this code and you'll see that you get three 1D object arrays, not 2D arrays of something else. But if you don't know how to determine that for yourself, and don't want to just try it and test, you can always explicitly specify the dtype (either as object, or as a complex type) to force it.

And meanwhile, even if you did need to pre-create the arrays (which, again, you don't), there would still be no reason to create dozens of separate variables and then put them in a dict after the fact. In other words, just do this:

arrays = np.zeros((3,2), dtype=np.object)
data = dict(zip(string.ascii_lowercase, arrays))

Or, if you really want entirely separate arrays for some reason instead of sliced out of a higher-dimensional array:

arrays = [np.zeros((2,), dtype=np.object) for _ in range(3)]
data = dict(zip(string.ascii_lowercase, arrays))

And of course you can wrap this all up in a function if you're doing it multiple times.

2 Comments

Thanks! The problem is that it's probably safer to initialize the arrays before assigning values to them, as described in this post, hence my insistence in having pre-initialized arrays.
@HappyPy: Your insistence is unnecessary. In cases like your c, when you're not sure whether NumPy is going to give you a 1D object array or a 2D int array but you want the former, you can just explicitly specify a dtype (either object, or a complex type).
0

The below is absolutely not recommended, but cool nevertheless

locals().update({
    'x%d' % i : np.zeros((2,), dtype=np.object)
    for i in range(3)
})
print x2
=> array([0, 0], dtype=object)

But, again, don't do it. Use a list (for example) instead.

4 Comments

Why isn't it recommended?
For one, explicit is better than implicit. Your code should be easy to read and understand. This code isn't.
@shx2: Try using the code inside a function. It won't work. Python usually doesn't actually use the locals dict for local variable lookup. Global scope is one of the cases where it does use the dict.
Also, if this initialization happens to be the only assignment to these variables, Python won't even realize they exist, and will treat the names as global, resulting in NameErrors when you try to access them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.