90

I want to combine 2 parts of the same array to make a complex array:

Data[:,:,:,0] , Data[:,:,:,1] 

These don't work:

x = np.complex(Data[:,:,:,0], Data[:,:,:,1]) x = complex(Data[:,:,:,0], Data[:,:,:,1]) 

Am I missing something? Does numpy not like performing array functions on complex numbers? Here's the error:

TypeError: only length-1 arrays can be converted to Python scalars 

9 Answers 9

108

This seems to do what you want:

numpy.apply_along_axis(lambda args: [complex(*args)], 3, Data) 

Here is another solution:

# The ellipsis is equivalent here to ":,:,:"... numpy.vectorize(complex)(Data[...,0], Data[...,1]) 

And yet another simpler solution:

Data[...,0] + 1j * Data[...,1] 

PS: If you want to save memory (no intermediate array):

result = 1j*Data[...,1]; result += Data[...,0] 

devS' solution below is also fast.

Sign up to request clarification or add additional context in comments.

11 Comments

Same error I'm afraid: TypeError: only length-1 arrays can be converted to Python Scalars
@Duncan: I updated the original answer after performing the test. It seems to be working, now.
thanks alot that does work. It's VERY slow though (as you might expect - as it's not really a numpy function), it takes 5 seconds per loop now instead of 0.1
@Duncan: I added two other solutions: it may be worth to time them too. If this works for you, please thumb up the answer!
Excellent they're both much faster :)
|
63

There's of course the rather obvious:

Data[...,0] + 1j * Data[...,1] 

Comments

33

If your real and imaginary parts are the slices along the last dimension and your array is contiguous along the last dimension, you can just do

A.view(dtype=np.complex128) 

If you are using single precision floats, this would be

A.view(dtype=np.complex64) 

Here is a fuller example

import numpy as np from numpy.random import rand # Randomly choose real and imaginary parts. # Treat last axis as the real and imaginary parts. A = rand(100, 2) # Cast the array as a complex array # Note that this will now be a 100x1 array A_comp = A.view(dtype=np.complex128) # To get the original array A back from the complex version A = A.view(dtype=np.float64) 

If you want to get rid of the extra dimension that stays around from the casting, you could do something like

A_comp = A.view(dtype=np.complex128)[...,0] 

This works because, in memory, a complex number is really just two floating point numbers. The first represents the real part, and the second represents the imaginary part. The view method of the array changes the dtype of the array to reflect that you want to treat two adjacent floating point values as a single complex number and updates the dimension accordingly.

This method does not copy any values in the array or perform any new computations, all it does is create a new array object that views the same block of memory differently. That makes it so that this operation can be performed much faster than anything that involves copying values. It also means that any changes made in the complex-valued array will be reflected in the array with the real and imaginary parts.

It may also be a little trickier to recover the original array if you remove the extra axis that is there immediately after the type cast. Things like A_comp[...,np.newaxis].view(np.float64) do not currently work because, as of this writing, NumPy doesn't detect that the array is still C-contiguous when the new axis is added. See this issue. A_comp.view(np.float64).reshape(A.shape) seems to work in most cases though.

1 Comment

+1: Very lucid explanation of the limitations of the method. You might want to add explicitly another limitation (shared memory between A_comp and A), and also an advantage of this method (speed).
20

This is what your are looking for:

from numpy import array a=array([1,2,3]) b=array([4,5,6]) a + 1j*b ->array([ 1.+4.j, 2.+5.j, 3.+6.j]) 

2 Comments

This is only a partial duplicate of earlier answers like Pierre GM's or mine: I think that its only effect is to take people's time for almost no added value (beyond the example), so I would suggest that you delete it.
14 people disagree! While the depth is weak and it doesn't deserve a checkmark, this example got me fastest to what I needed.
18

I am python novice so this may not be the most efficient method but, if I understand the intent of the question correctly, steps listed below worked for me.

>>> import numpy as np >>> Data = np.random.random((100, 100, 1000, 2)) >>> result = np.empty(Data.shape[:-1], dtype=complex) >>> result.real = Data[...,0]; result.imag = Data[...,1] >>> print Data[0,0,0,0], Data[0,0,0,1], result[0,0,0] 0.0782889873474 0.156087854837 (0.0782889873474+0.156087854837j) 

5 Comments

Interesting idea. However, the question is about combining Data[:,:,:,0] and Data[:,:,:,1] (more complicated than your a). Also, instead of using zeros(), you should use the faster and more appropriate empty().
I compared it with the Data[…,0] + 1j * Data[…,1] solution. With Data = random.rand(100,100,1000,2),c=zeros(a.shape[:-1],dtype=complex);c.real = Data[...,0]; c.imag = Data[...,1]; is 2x faster than the straightforward Data[…,0] + 1j * Data[…,1]. Surprisingly, the effect of using empty instead of zeros was negligible.
+1. Note: I get the same speed with a variation of my last answer: result = 1j*Data[...,1]; result += Data[...,0]. This answer is more natural, though, if a single formula is not used.
I think this is the best answer because the intent is obvious when reading the code; Eric's answers, while functionally correct, are less clear when reading back over the code.
Well in the underlying framework, there will be a C call to malloc or calloc likely in either case. Now calloc would do a memset to 0 but the time of this operation is extremely fast given it can be done with a single x86 assembly instruction e.g. REP SETZ, mostly just the memory latency might make it take some time, but likely not significant. However numpy may zero for empty anyway making it an alias in effect.
6
import numpy as np n = 51 #number of data points # Suppose the real and imaginary parts are created independently real_part = np.random.normal(size=n) imag_part = np.random.normal(size=n) # Create a complex array - the imaginary part will be equal to zero z = np.array(real_part, dtype=complex) # Now define the imaginary part: z.imag = imag_part print(z) 

Comments

1

I use the following method:

import numpy as np real = np.ones((2, 3)) imag = 2*np.ones((2, 3)) complex = np.vectorize(complex)(real, imag) # OR complex = real + 1j*imag 

Comments

0

If you really want to eke out performance (with big arrays), numexpr can be used, which takes advantage of multiple cores.

Setup:

>>> import numpy as np >>> Data = np.random.randn(64, 64, 64, 2) >>> x, y = Data[...,0], Data[...,1] 

With numexpr:

>>> import numexpr as ne >>> %timeit result = ne.evaluate("complex(x, y)") 573 µs ± 21.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

Compared to fast numpy method:

>>> %timeit result = np.empty(x.shape, dtype=complex); result.real = x; result.imag = y 1.39 ms ± 5.74 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

Comments

-1

That worked for me:

input:

[complex(a,b) for a,b in zip([1,2,3],[1,2,3])] 

output:

[(1+4j), (2+5j), (3+6j)] 

1 Comment

You are creating a list and not a numpy array

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.