0

I have to fill np array in row 2 with values from row 0. Array looks like that:

[[3. 4. 5. 6. 7. 8. 9. 8. 7. 6.]
 [0. 0. 1. 0. 0. 1. 0. 0. 0. 0.]
 [0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]] 

Finally it should looks like bellow

[[3. 4. 5. 6. 7. 8. 9. 8. 7. 6.]
 [0. 0. 1. 0. 0. 1. 0. 0. 0. 0.]
 [5. 5. 5. 8. 8. 8. 6. 6. 6. 6.]]

That means:

First row determine points of change. From beginning of row 3 to place where first value "1" in row 2, the row 3 should have value from row 0 where in row two occurs first value "1" and so on..

There is something similar but not exactly what i need. Fill values in numpy array that are between a certain value

Do you know how to do it without iterate over columns?

5
  • It would be helpful if you showed the logic, i.e. please include a version of the code that iterates over the columns. Commented Feb 21, 2024 at 22:19
  • 1
    Can you please provide that data as valid code? Commented Feb 21, 2024 at 22:25
  • If the 1s there are irregularly spaced, then I'm not convinced there is a way to do it without iterating. Commented Feb 21, 2024 at 22:25
  • 1
    @TimRoberts But you can let NumPy do the iterating. Commented Feb 21, 2024 at 22:26
  • Can the last value in the middle row be 1? If yes, what shall the result be? Commented Feb 22, 2024 at 1:55

3 Answers 3

2

Try this:

import numpy as np

arr = np.array([[3., 4., 5., 6., 7., 8., 9., 8., 7., 6.],
 [0., 0., 1., 0., 0., 1., 0., 0., 0., 0.],
 [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

lastitem = arr[1,-1]
arr[1, -1] = 1.

vals = arr[0, arr[1].astype(bool)][::-1]
idxs = arr[1].astype(int)[::-1].cumsum() - 1

arr[1, -1] = lastitem
arr[2] = vals[idxs][::-1]

print(arr)

Output:

[[3. 4. 5. 6. 7. 8. 9. 8. 7. 6.]
 [0. 0. 1. 0. 0. 1. 0. 0. 0. 0.]
 [5. 5. 5. 8. 8. 8. 6. 6. 6. 6.]]

Basic idea is to use arr[1] first as boolean array to extract the items of arr[0] which will be really used to vals. vals is reversed.

Then arr[1] is converted to int, reversed, cumulative sum applied and adjusted so that it finally is an array of indexes into vals named idxs.

Last step is to convert the indexes into actual values and set arr[2] accordingly.

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

Comments

1

using indexing

You can get the negative indices at the positions of the 1s (and the last), then backfill using a reverse cumulated minimum:

idx = np.minimum.accumulate(np.where(a[1], np.arange(-a.shape[1], 0), -1)[::-1])[::-1]
# array([-8, -8, -8, -5, -5, -5, -1, -1, -1, -1])
a[2] = a[0, idx]

Step by step intermediates to construct idx (where _ denote the previous command):

np.arange(-a.shape[1], 0)
# array([-10,  -9,  -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1])

np.where(a[1], _, -1)
# array([-1, -1, -8, -1, -1, -5, -1, -1, -1, -1])

np.minimum.accumulate(_[::-1])[::-1]
# array([-8, -8, -8, -5, -5, -5, -1, -1, -1, -1])

Same logic with positive indexing:

idx = np.minimum.accumulate(
    np.where(a[1], np.arange(a.shape[1]), a.shape[1] - 1)[::-1]
)[::-1]

a[2] = a[0, idx]

Intermediates:

np.arange(a.shape[1])
# array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

np.where(a[1], _, a.shape[1]-1)
# array([9, 9, 2, 9, 9, 5, 9, 9, 9, 9])

np.minimum.accumulate(_[::-1])[::-1]
# array([2, 2, 2, 5, 5, 5, 9, 9, 9, 9])

using numpy.repeat:

Here we get the indices of the 1s (and the last position), compute the number of items to repeat

idx = np.where(np.r_[a[1, :-1], 1])[0]
# array([2, 5, 9])

a[2] = np.repeat(a[0, idx], np.diff(np.r_[-1, idx]))
# np.repeat([5, 8, 6], [3, 3, 4])

using 's bfill:

import pandas as pd

a[2] = pd.Series(a[0]).where(np.r_[a[1, :-1]==1, True]).bfill()

Output

array([[3, 4, 5, 6, 7, 8, 9, 8, 7, 6],
       [0, 0, 1, 0, 0, 1, 0, 0, 0, 0],
       [5, 5, 5, 8, 8, 8, 6, 6, 6, 6]])

1 Comment

Solution with np.repeat is ok. It seems to work fine. I will use it.
1

Use assignment:

arr[2] = np.r_[arr[0,arr[1]==1], arr[0,-1]][np.roll(arr[1],1).cumsum(dtype=int)]
arr 

array([[3., 4., 5., 6., 7., 8., 9., 8., 7., 6.],
       [0., 0., 1., 0., 0., 1., 0., 0., 0., 0.],
       [5., 5., 5., 8., 8., 8., 6., 6., 6., 6.]])

6 Comments

If you delete another space character, it'll even fit PEP 8's 79 characters line limit.
Fails if I change the middle row's last value to 1.
@KellyBundy the last value cannot be 1. ie how will that be a point of change?. Try any other place besides the last value
@KellyBundy also a 1 in the end is alittle ambiguous. ie in the example above, what would be the output if there was a 1 at the end?
Maybe. I've asked 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.