0

I have the the following array

import numpy as np
import CustomClass

# Total = 24
height = 4
width  = 6
a = np.empty((height, width), dtype=object)

for row in range(self.height):
    for col in range(self.width):
        self.a[row, col] = CustomClass()

I would like to change the attribute for part of the array (filled with my custom class). For example, I would like to change 12 elements starting from element[0,0], then from row to row. Ideally, I would do something comparable to

change=11
for i in range(change):
   a[i].value=True

The result would be something like the following (0: unchanged, x: changed)

x x x x x x 
x x x x x 0
0 0 0 0 0 0
0 0 0 0 0 0

The problem with my array is that I first need to flatten it before I could do something like this. Or I should calculate how many columns and rows I before I can index the array itself. Is there a numpy function such that I can just iteratre over the array element by element (row by row).

I hope my explanation is clear?

6
  • If your numpy is 2-d, then you can calculate the mod of (number of rows) and also columns to do that. But any specific reason not to use flatten and reshape? Commented Dec 14, 2021 at 9:35
  • 1
    Reshaping or flattening the array doesnt change it in memory, it only changes the metadata. You can flatten and reshape as much as you like without performance hits. Commented Dec 14, 2021 at 9:36
  • Perforamance and trying to learn new functions were the reason to ask this question. But It seems that flatten and reshape is enough. However, I still need to use a for loop to change te values. Any suggestions to do this differently or can I not avoid the for loop Commented Dec 14, 2021 at 9:41
  • After you flatten you can use slicing like full_array[0:change] = new_values as long as the shapes match Commented Dec 14, 2021 at 9:42
  • Thank you for the suggestion, however my array consist of objects from which I would like to change the attribute and not replace it with new_values Commented Dec 14, 2021 at 9:45

2 Answers 2

2

You can do this a couple of ways, but one approach would be to use np.unravel_index to get the two-dimensional indices that correspond to the flattened one-dimensional ones. I'll show you how to do this below.

First create your data:

from dataclasses import dataclass
import numpy as np

height = 4
width  = 6

@dataclass
class myClass:
    value: bool = False
        
data = np.asarray([myClass() for _ in range(height * width)])
data = data.reshape(height, width)

Now, make change an array of indices that you want to change, rather than the number of elements you want to change:

change = np.arange(11)

Determine the two-dimensional indices that correspond to those in change:

rows, columns = np.unravel_index(indices=change, shape=(height, width))

And then you can use these indices to iterate over the elements you want to change:

for element in data[rows, columns]:
    element.value = True
Sign up to request clarification or add additional context in comments.

1 Comment

I like this answer as it learned me something new (np.ravel_index) and gets the job done. Thanks
0

You can use variable to count how many elements was changed and use break to exit loops

import numpy as np


height = 4
width  = 6
a = np.empty((height, width), dtype=object)
a[:] = '0'

count = 0

for row in range(height):
    for col in range(width):
        count += 1
        a[row, col] = 'x'
        if count == 11:
            break
    if count == 11:
       break
    
print(a)        

Result:

[['x' 'x' 'x' 'x' 'x' 'x']
 ['x' 'x' 'x' 'x' 'x' '0']
 ['0' '0' '0' '0' '0' '0']
 ['0' '0' '0' '0' '0' '0']]

To make it simpler you can set count = 11 at start and later substract until you get 0.

This way you have to set 11 only in one place.

import numpy as np

height = 4
width  = 6
a = np.empty((height, width), dtype=object)
a[:] = '0'

count = 11

for row in range(height):
    for col in range(width):
        count -= 1
        a[row, col] = 'x'
        if count == 0:
            break
    if count == 0:
       break
    
print(a)        

EDIT:

You could also calculate how many full rows you have to change and how many elements you have to change in last row.

import numpy as np

height = 4
width  = 6
a = np.empty((height, width), dtype=object)
a[:] = '0'

count = 11

rows = count // width
rest = count % width    

a[:rows] = 'x'
a[rows,:rest] = 'x'

print(a)

But this will not work if you want to start in different place then [0,0]

4 Comments

Using nested loops doesn't sound very numpy-esque at all...
@AKX if it would create rectangle then it could use numpy function for this. But meanwhile I created other method which use more numpy-esque method a[:rows] = 'x' a[rows,:rest] = 'x'
That seems better already. You can do rows, rest = divmod(count, width), though. ;-)
Thank you for the suggestion, I like the small edited version. Interesting, especially with the divmod. However, I'm changing an attribute and not setting an value. Will that work? object.value = True, is the goal

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.