[ Python 3.1 Grid Simulation Question ]
Based on the answers from this question, I have created a grid from a 1D array, which modifies a value and its surrounding neighbours; if that value is found in another array. The main value is converted by a specific % and the surrounding elements by another.
However, how do I ensure that the changed values are not converted again during the next iteration of the samples.
Example code below. Thank you for your time!
import numpy as np
def grid(array,samples,details):
#Sides of the square (will be using a squarable number
Width = (len(array)) ** 0.5
#Convert to grid
Converted = array.reshape(Width,Width)
#Conversion details
Change = [details[1]] + [details[2]]
nrows, ncols = Converted.shape
for value in samples:
#First instance where indexing returns it
i,j = np.argwhere(Converted == value)[0]
#Prevent indexing outside the boudaries of the
#array which would cause a "wraparound" assignment
istart, istop = max(i-1, 0), min(i+2, nrows)
jstart, jstop = max(j-1, 0), min(j+2, ncols)
#Set the value within a 3x3 window to their "new_value"
for elem in Converted[istart:istop, jstart:jstop]:
Converted[elem] = elem + (elem * (value * ((Change[1]/100))
#Set the main value to the new value
Converted[i,j] = value + (value * ((Change[0])/100))
#Convert back to 1D list
Converted.tolist()
return (Converted)
a = [16,2,20,4,14,6,70,8,9,100,32,15,7,14,50,20,17,10,9,20,7,17,50,2,19,20,21,22,23,24,25]
samples = [2, 7]
grid_details = [10,50,100]
result = grid(a,samples,grid_details)
Answer 1
First off, let's walk through your code section by section... There are several problems with what you have written now...
Incidentally, there's a loose convention in python to reserve Uppercase and CamelCased names for classes. (You'll often see code like foo = FooBar(baz)
, where foo
is an instance of the class FooBar
.) There's nothing wrong with calling your variable Converted
instead of converted
, but most code you'll see will use the latter form.
So let's start here:
import numpy as np
def grid(array,samples,details):
...
It appears you're passing a list in as array
later on. Because of this, you need to convert the list array
to a numpy.ndarray
. (Otherwise, things like array.reshape
aren't going to work.) You might also consider changing your variable name to something less generic than "array
". For the moment, though, let's keep the variable names the same. I'm also going to assume that you want floating point values, as you're multiplying by percentages later on... Let's change this to:
import numpy as np
def grid(array,samples,details):
array = np.asarray(array, dtype=np.float)
...
Moving on...
...
#Sides of the square (will be using a squarable number
Width = (len(array)) ** 0.5
#Convert to grid
Converted = array.reshape(Width,Width)
...
You have a math problem there!! A 2x2 array as 4 elements, a 3x3 array has 9, a 4x4 array will have 16, and so on... If you want to reshape a sequence into a square grid, you need to take the square root, not divide by 2! Let's change that to:
...
#Sides of the square (will be using a squareable number)
Width = int(np.sqrt(array.size))
#Convert to grid
Converted = array.reshape(Width,Width)
...
Blame it on a lack of sleep... Clearly, x**0.5
is the same as sqrt(x)
, I just didn't see the second *
. Sorry about that!
Next:
...
Change = [details[1]] + [details[2]]
...
This is the same as just doing this:
Change = details[1:3]
The portion after this is fine, so let's jump on to the next problem:
...
#Set the value within a 3x3 window to their "new_value"
for elem in Converted[istart:istop, jstart:jstop]:
Converted[elem] = elem + (elem * (value * ((Change[1]/100))
#Set the main value to the new value
Converted[i,j] = value + (value * ((Change[0])/100))
...
First off, when you iterate over it, elem
is the value not the index! You can't index the array by the values you get, otherwise you'd be trying to index it by things like 0.01
, pi
, or maybe even complex numbers like 5.6 + 98.44j
. Second, you're using numpy for a reason... There's no reason to iterate through each element like this. Third, you're changing the center value twice, which is almost definitely not what you want. Instead, we can just do this:
...
#--Set the value within a 3x3 window to their "new_value"
# Save the "center" value for later use
center = Converted[i,j]
# Adjust the pixels around the center by a percentage
Converted[istart:istop] *= 1 + Change[1] / 100.0
# Adjust the center pixel by a different percentage
Converted[i,j] = center * (1 + Change[0] / 100.0)
...
Finally, you have a problem with the length of the list "array
"that you're passing in...
a = [16,2,20,4,14,6,70,8,9,100,32,15,7,14,50,20,17,10,9,20,7,17,50,2,19,20,21,22,23,24,25]
That's a 31 element array... There's no way to make that square without truncating or adding values. Your current code would try to turn it into a 15x15 array, which would result in an error (a 15x15 matrix would need 255 values, (15**2
)). I'm going to assume that you wanted a 25-element, 5x5 array instead. Let's replace that with:
a = [16,2,20,4,14,6,70,8,9,100,32,15,7,14,50,20,17,10,9,20,7,17,50,2,19]
Okay, so let's put all those suggestions together into a workable piece of code:
import numpy as np
def grid(array,samples,details):
array = np.asarray(array, dtype=np.float)
#Sides of the square (will be using a squarable number
Width = int(np.sqrt(array.size))
#Convert to grid
Converted = array.reshape(Width,Width)
#Conversion details
Change = details[1:3]
nrows, ncols = Converted.shape
for value in samples:
#First instance where indexing returns it
i,j = np.argwhere(Converted == value)[0]
#Prevent indexing outside the boudaries of the
#array which would cause a "wraparound" assignment
istart, istop = max(i-1, 0), min(i+2, nrows)
jstart, jstop = max(j-1, 0), min(j+2, ncols)
#Set the value within a 3x3 window to their "new_value"
center_value = Converted[i,j]
Converted[istart:istop, jstart:jstop] *= 1 + Change[1] / 100.0
Converted[i,j] = center_value * (1 + Change[0] / 100.0)
#Convert back to 1D list
Converted.tolist()
return Converted
a = [16,2,20,4,14,6,70,8,9,100,32,15,7,14,50,20,17,10,9,20,7,17,50,2,19]
samples = [2, 7]
grid_details = [10,50,100]
result = grid(a,samples,grid_details)
print(result)
So, this transforms your original array:
[[ 16. 2. 20. 4. 14.]
[ 6. 70. 8. 9. 100.]
[ 32. 15. 7. 14. 50.]
[ 20. 17. 10. 9. 20.]
[ 7. 17. 50. 2. 19.]]
Into:
[[ 32. 3. 40. 4. 14. ]
[ 12. 280. 32. 18. 100. ]
[ 32. 30. 10.5 28. 50. ]
[ 20. 34. 20. 18. 20. ]
[ 7. 17. 50. 2. 19. ]]
Okay. Now for what I think you were originally asking... In this case, the item in the second row, second column gets modified twice... Once due to the 2 in the first row, second column, and once due to the 7 in the third row, third column.
Is this what you wanted to avoid?? If so, what do you want to happen in this case?
Do you want it to only be modified by the first match? The second match? Modified both times, but only have it modified by the sum of the percentages? You need to define what you want to happen.
At any rate, hope that helps!
Edit
If you want to avoid matching a newly-modified value, you can just find all of the matches before you start modifying things. For example, if we change this part:
for value in samples:
#First instance where indexing returns it
i,j = np.argwhere(Converted == value)[0]
To this:
locations = [np.argwhere(Converted == value)[0] for value in samples]
for i,j in locations:
...
It should do what you want. Hope that's clear!