This is similar to this so please read it first to understand what I am trying to do.
Now, I want to make the replacement when I have class instances.Something like:
Make the replacement block like this. Also note this assumes your arr will be strictly in multiples of 5 and there will be as many blocks like b1,b2 etc as there are blocks in arr as given in the example.
for i,b_arr in enumerate(d):
temp_arr = []
for j in range(5):
if j<len(b_arr)
temp_arr.append(B(arr[i,j],b_arr[j].b))
else:
temp_arr.append(B(arr[i,j],b_arr[-1].b))
d[i] = np.array(temp_arr) ## not sure if this step is right, not too familiar with numpy.
So I add
print(d.shape)
print(d)
and get
2249:~/mypy$ python3 stack42283851.py
(2, 1, 4)
[[[<__main__.B object at 0xb71d760c> <__main__.B object at 0xb71d7aac>
<__main__.B object at 0xb71d7acc> <__main__.B object at 0xb71e5cec>]]
[[<__main__.B object at 0xb391718c> <__main__.B object at 0xb39171ac>
<__main__.B object at 0xb39171cc> <__main__.B object at 0xb39171ec>]]]
adding a __repr__
to B
I get
1231:~/mypy$ python3 stack42283851.py
(2, 1, 4)
[[[B(100, a) B(11, b) B(300, c) B(33, d)]]
[[B(45, a) B(65, b) B(77, c) B(88, d)]]]
Adding
import itertools
for a,b in itertools.zip_longest(arr[0,:],b1):
print(a,b)
produces
1 B(100, a)
2 B(11, b)
3 B(300, c)
4 B(33, d)
5 None
Changing that to:
newlist = []
for a,b in itertools.zip_longest(arr[0,:],b1):
if b is not None:
new_b = B(a, b.b)
last_b = b
else:
new_b = B(a, last_b.b)
newlist.append(new_b)
print(np.array(newlist))
produces
[B(1, a) B(2, b) B(3, c) B(4, d) B(5, d)]
Assign that to b1
, and repeat for a[1,:]
and b2
.
To be a little cleaner I could write that new_b
code as a function, and rewrite the loop as a list comprehension.
Yes, I could modify b
in place, e.g.
b.a = a
but since I need to create a new B
object to replace the None
, why bother. I can't add the new B
object to the original b1
array. So it is simpler to create a new array via a list.
I can do an in-place change of d
and b1
with:
def replace(a,b):
b.a = a
f = np.frompyfunc(replace, 2, 1)
f(arr[:,None,:4], d) # produces array of None; ignore
print(d)
print(b1)
[[[B(1, a) B(2, b) B(3, c) B(4, d)]] # chgd d
[[B(6, a) B(7, b) B(8, c) B(9, d)]]]
[B(1, a) B(2, b) B(3, c) B(4, d)] # chgd b1
I'm just using frompyfunc
as a lazy mans way of broadcasting arr
against d
and iterating over all elements. Note that I have to change arr
to match d
shape. Also this does not add any new B()
. Obviously you can't do that in-place.
My B
is
class B():
def __init__(self, a,b):
self.a = a
self.b = b
def __repr__(self):
return 'B(%s, %s)'%(self.a, self.b)
Playing with frompyfunc
some more:
getB_b = np.frompyfunc(lambda x: x.b, 1,1) # fetch b attributes
print(getB_b(d))
#[[['a' 'b' 'c' 'd']]
#
# [['a' 'b' 'c' 'd']]]
mkB = np.frompyfunc(B, 2,1) # build array of B() with broadcasting
print(mkB(arr, ['a','b','c','d','e']))
# [[B(1, a) B(2, b) B(3, c) B(4, d) B(5, e)]
# [B(6, a) B(7, b) B(8, c) B(9, d) B(10, e)]]
print(mkB(arr[:,:4], getB_b(d[:,0,:])))
# [[B(1, a) B(2, b) B(3, c) B(4, d)]
# [B(6, a) B(7, b) B(8, c) B(9, d)]]
edit for comments
arr1 = np.array([ [1,2],[6,7] ])
newlist = []
for a,b in itertools.zip_longest(arr1[0,:],b1):
if b is not None:
new_b = B(a, b.b)
last_b = b
else:
new_b = B(a, last_b.b)
newlist.append(new_b)
print(np.array(newlist))
produces
[B(1, a) B(2, b) B(None, c) B(None, d)]
When arr
is shorter, a
will be None
(instead of b
); so we need to test for that
def foo(arr,bn):
newlist = []
for a,b in itertools.zip_longest(arr,bn):
print(a,b)
if a is None:
pass
else:
if b is not None:
new_b = B(a, b.b)
last_b = b
else:
new_b = B(a, last_b.b)
newlist.append(new_b)
return newlist
print(np.array(foo(arr1[0,:],b1))) # arr1 shorter
print(np.array(foo(arr[0,:], b2))) # arr longer
testing:
1 B(1, a)
2 B(2, b)
None B(3, c)
None B(4, d)
[B(1, a) B(2, b)]
1 B(6, a)
2 B(7, b)
3 B(8, c)
4 B(9, d)
5 None
[B(1, a) B(2, b) B(3, c) B(4, d) B(5, d)]
Nothing special or magical; just a matter making sure I get the if
tests and indentation right.