2

I am having a terrible time trying to replace values in a numpy array and running up against a very strange behavior I was hoping someone could explain. Essentially I want to do a crossing over operation in a genetic algorithm. Here's a simple example. I have a 2 X 10 array, and want all the values in row 1 up to column 5 to be swapped with the values in row 2 up to column 5. Here's the code:

z=np.random.uniform(low=0,high=1,size=(2,10))
zcopy = z
print z

[[ 0.77488523  0.39966358  0.63233664  0.77093136  0.04102615  0.98984184
0.43402537  0.0910648   0.28037032  0.76654885]
[ 0.49980878  0.28161905  0.71972029  0.01208004  0.87851569  0.16853681
0.96325992  0.90886083  0.12344231  0.83665396]]

z[1,range(4)] = zcopy[0,range(4)]
print z

[[ 0.77488523  0.39966358  0.63233664  0.77093136  0.04102615  0.98984184
0.43402537  0.0910648   0.28037032  0.76654885]
[ 0.77488523  0.39966358  0.63233664  0.77093136  0.87851569  0.16853681
0.96325992  0.90886083  0.12344231  0.83665396]]

As you can see it's just copied all of row 1 into both rows. But, if I don't specify a subset of another array but just give it say integers it works perfectly

z[1,range(4)] = range(4)
print z
[[ 0.77488523  0.39966358  0.63233664  0.77093136  0.04102615  0.98984184
0.43402537  0.0910648   0.28037032  0.76654885]
[ 0.          1.          2.          3.          0.87851569  0.16853681
0.96325992  0.90886083  0.12344231  0.83665396]]

I'm rather perplexed. Does anyone have any idea how to work around this?

emhart
  • 804
  • 5
  • 9
  • I don't see that it has copied all of row 1 into both rows. The numbers are different after the second `print z`. Can you clarify what you mean? – tinman Jul 05 '12 at 21:36
  • @tinman, look again. In the first example, the numbers in the first four columns are identical after `z[1,range(4)] = zcopy[0,range(4)]`. – senderle Jul 05 '12 at 21:42
  • @senderle: that's what you'd expect isn't it? The op said `it's just copied all of row 1 into both rows` but it's not all of row 1, it's only the first 4 columns. But his comment further down about why zcopy has changed too makes me think I've missed something. – tinman Jul 05 '12 at 22:19
  • @tinman, hmm, you're right, that is a bit confusing. – senderle Jul 06 '12 at 00:26

2 Answers2

3

try this

z=np.random.uniform(low=0,high=1,size=(2,10))
z[:,range(4)] = z[::-1,range(4)]

before

[[ 0.30778241  0.04832341  0.616925    0.81325565  0.44578265  0.59024722 0.32147695  0.68434105  0.47430297  0.06256859]
 [ 0.58522801  0.23922353  0.15388696  0.46400394  0.33126422  0.54651948 0.34724277  0.46974174  0.68646707  0.62549495]]

after

[[ 0.58522801  0.23922353  0.15388696  0.46400394  0.44578265  0.59024722 0.32147695  0.68434105  0.47430297  0.06256859]
 [ 0.30778241  0.04832341  0.616925    0.81325565  0.33126422  0.54651948 0.34724277  0.46974174  0.68646707  0.62549495]]
nye17
  • 12,857
  • 11
  • 58
  • 68
  • 1
    @DistribEcology good question! `::-1` basically give you a reversed indexing: from going [0,1] to [1,0]. – nye17 Jul 05 '12 at 21:50
  • 1
    Take a look at [this post](http://stackoverflow.com/q/509211/577088) for more information about slicing -- which is what that's called. Also, you should probably take a look at [the tutorial](http://docs.python.org/tutorial/introduction.html#an-informal-introduction-to-python) if you haven't encountered slicing yet. – senderle Jul 05 '12 at 21:52
2

There seem to be two questions here.

  1. "Why doesn't zcopy = z make a copy?"
  2. "Why doesn't z[1,range(4)] = zcopy[0,range(4)] swap the values in the first four columns?"

The answer to the first question is that assigning a value to a variable name in Python doesn't make a copy. A variable in Python is just a label for an object; giving the object a new label doesn't change the object itself at all. If you want to make a copy of a numpy array, specifically, you can use the copy method, which returns a new copy of the array. As in:

zcopy = z.copy()

The answer to the second question is that your code only assigns one set of values: z[1, range(4)]. If you want to change both rows, you have to assign to both rows! In many languages you'd do this with a tmp variable, but python provides an elegant way to swap values without needing temporary variables:

>>> z[1, range(4)], z[0, range(4)] = z[0, range(4)], z[1, range(4)]

nye17's answer is a bit cleaner, but it does the same thing.

Community
  • 1
  • 1
senderle
  • 145,869
  • 36
  • 209
  • 233
  • Right, this works, but a big part of my question is why do the values of zcopy get changed to be the same as z in my original question? I can't figure that out. – emhart Jul 05 '12 at 21:47
  • 1
    Oh, well `zcopy` isn't really a copy, that's why; I didn't realize that was part of your question. Assigning an object to a variable name never makes a copy in Python. Both `z` and `zcopy` refer to the same numpy object. If you want to make a copy of `z`, use `z.copy()`. But you'll still have the same problem, because you're overwriting one row but leaving the other unchanged. – senderle Jul 05 '12 at 21:48
  • @DistribEcology zcopy isn't a `physical` copy in the sense that it only provides as reference to the same memory that stores the z array. – nye17 Jul 05 '12 at 21:51
  • Ahh, that explains it. I'm moving to numpy from R and falling into the trap of trying to do this in python the exact same way I would in R. Thanks for the tips. – emhart Jul 05 '12 at 22:01
  • @DistribEcology on a side note, it's a rare event that ppl in bio statistics convert from R to Python....;-) – nye17 Jul 05 '12 at 22:20