I am trying to create a function that imprints a smaller array onto another. The dimensions and center are arbitrary. For example I may want to put a 3x3 on a 5x5 at center (1, 2) or I may want to put a 5x5 on a 100x100 at center (50, 30). Ignoring indexing errors and even arrays that have no center.
Example:
arr1 =
[2, 3, 5]
[1, 5, 6]
[1, 0, 1]
arr2 =
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
arr3 = imprintarray(arr1, arr2, (1, 2))
arr3 =
[0, 2, 3, 5, 0]
[0, 1, 5, 6, 0]
[0, 1, 0, 1, 0]
[0, 0, 0, 0, 0]
[0, 0, 0, 0, 0]
I call the first array the smallarray (the one that is imprinted) and the second array to be the map array (the bigger array who has its values modified)
My solution was to create a 3rd array with the target indexes on the the maparray and to iterate through accessing the smallarrays values and changing the elements of the maparray directly.
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.zeros((3, 3))
smallarray[:] = 2
def createindexarray(dimensions, center):
array = []
adjustment = (dimensions[0] * -0.5) + 0.5
for row in range(dimensions[0]):
elements = []
for col in range(dimensions[1]):
elements.append((row + center[0] + int(adjustment), col + center[1] + int(adjustment)))
array.append(elements)
return array
indexarray = createindexarray((3, 3), (3, 5))
for w, x in enumerate(smallarray):
for y, z in enumerate(x):
maparray[indexarray[w][y][0]][indexarray[w][y][1]] = smallarray[w][y]
It does feel like there should be a better way or more efficient way. I looked through numpy's documentation to see if I could find something like this but I could not find it. Thoughts? Even if you think this is the best way any tips on improving my Python would be much appreciated.
Here is my adjustment to #Shahab Rahnama's solution that accounts for #Mechanic Pig's comment about the shift only working for (3x3) matrices, and my comment about the code not working if the matrix is placed with some elements out of bounds.
import numpy as np
def imprint(center, bigarray, smallarray):
half_height = smallarray.shape[0] // 2
half_width = smallarray.shape[1] // 2
top = center[0] - half_height
bottom = center[0] + half_height + 1
left = center[1] - half_width
right = center[1] + half_width + 1
bigarray[ \
max(0, top): \
min(bigarray.shape[0], bottom),
max(0, left): \
min(bigarray.shape[1], right)
] = smallarray[ \
(top < 0) * abs(top): \
smallarray.shape[0] - (bottom > bigarray.shape[0]) * (bottom - bigarray.shape[0]), \
(left < 0) * abs(left): \
smallarray.shape[1] - (right > bigarray.shape[1]) * (right - bigarray.shape[1]) \
]
return bigarray
bigarray = np.zeros((7, 7))
smallarray = 2 * np.ones((5, 5))
imprint((3, 3), bigarray, smallarray)
Create index arrays using np.meshgrid and the smallarray's shape.
bigarray = np.zeros((9, 9))
smallarray = np.zeros((3, 3)) + 2
x,y = smallarray.shape
xv,yv = np.meshgrid(np.arange(x),np.arange(y))
#print(xv)
#print(yv)
Those can be used on the left-hand-side of an assignment to imprint the smallarray. Without modification the smallarray will be positioned in the upper-left corner.
bigarray[xv,yv] = smallarray
print(bigarray)
[[2. 2. 2. 0. 0. 0. 0. 0. 0.]
[2. 2. 2. 0. 0. 0. 0. 0. 0.]
[2. 2. 2. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]]
The position can be changed by adding a scalar to the index arrays.
bigarray[xv+2,yv+3] = smallarray + 4
print(bigarray)
[[2. 2. 2. 0. 0. 0. 0. 0. 0.]
[2. 2. 2. 0. 0. 0. 0. 0. 0.]
[2. 2. 2. 6. 6. 6. 0. 0. 0.]
[0. 0. 0. 6. 6. 6. 0. 0. 0.]
[0. 0. 0. 6. 6. 6. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]]
The scalars will be the index in the bigarray where the [0,0] point of the smallarray will be placed. If the function needs to accept the position of the center of the smallarray just use its shape to calculate the appropriate scalar. Also use both arrays' shape to calculate whether the small one will fit then adjust.
The index arrays can also be made with .indices.
xv,yv = np.indices(smallarray.shape)
Ignoring your requirement for "center" alignment1, let's say you want to place the values of some array a1 into another array a2 with some offset offset. offset is the coordinate in a2 where a1[0, 0, ..., 0] ends up. The values can be negative without wrapping.
The sizes of the arrays are irrelevant. We only need to assume that the types are at least somewhat compatible, and that a1.ndim == a2.ndim == len(offset). Let's call the number of dimensions d.
The goal is to be able to create a tuple of slices for each array, so that the assignment becomes a simple indexing operation:
a2[index2] = a1[index1]
So let's start writing this function:
def emplace(a1, a2, offset):
offset = np.asanyarray(offset)
if a1.ndim != a2.ndim or a1.ndim != len(offset) or len(offset) != offset.size:
raise ValueError('All dimensions must match')
First let's filter out the cases where there is no overlap at all:
if a1.size == 0 or a2.size == 0:
return
if (offset + a1.shape <= 0).any() or (offset >= a2.shape).any():
return
Now that you know that there will be overlap, you can compute the starting indices for both arrays. If offset is negative, that truncates leading elements from a1. If it's positive, it skips elements in a2.
start1 = np.where(offset < 0, -offset, 0)
start2 = np.where(offset < 0, 0, -offset)
The ends can be computed in a similar manner, except that the bound check is done on the opposite end now:
stop1 = np.where(offset + a1.shape > a2.shape, a2.shape - offset, a1.shape)
stop2 = np.where(offset + a1.shape > a2.shape, a2.shape, offset + a1.shape)
You can construct tuples of indices from the bounds:
index1 = tuple(slice(*bounds) for bounds in np.stack((start1, stop1), axis=-1))
index2 = tuple(slice(*bounds) for bounds in np.stack((start2, stop2), axis=-1))
And so the final emplacement becomes just a simple assignment:
a2[index2] = a1[index1]
TL;DR
Here is how I would write the full function:
def emplace(a1, a2, offset):
offset = np.asanyarray(offset)
if a1.ndim != a2.ndim or a1.ndim != len(offset) or len(offset) != offset.size:
raise ValueError('All dimensions must match')
if a1.size == 0 or a2.size == 0:
return
if (offset + a1.shape <= 0).any() or (offset >= a2.shape).any():
return
noffset = -offset
omask = offset < 0
start1 = np.where(omask, noffset, 0)
start2 = np.where(omask, 0, noffset)
omask = offset + a1.shape > a2.shape
stop1 = np.where(omask, a2.shape - offset, a1.shape)
stop2 = np.where(omask, a2.shape, offset + a1.shape)
index1 = tuple(slice(*bounds) for bounds in np.stack((start1, stop1), axis=-1))
index2 = tuple(slice(*bounds) for bounds in np.stack((start2, stop2), axis=-1))
a2[index2] = a1[index1]
1 I'm not suggesting you actually ignore how you want to do things. Just convert the center alignment to a corner offset.
Yes, there is a better way :
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.zeros((3, 3))
smallarray[:] = 2
def imprint(center, maparray, smallarray):
maparray[center[0]:center[0]+smallarray.shape[0],center[1]:center[1]+smallarray.shape[1]] = smallarray
return maparray
print(imprint((3, 5), maparray, smallarray))
I used roll to overcome out-of-bounds (if the center is on the edge like [7,7] and [4,7], the smallarray will be out-of-bounds):
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.zeros((3, 3))
smallarray[:] = 2
def imprint(center, maparray, smallarray):
rollx= 0
rolly= 0
if maparray.shape[0] - center[0] < smallarray.shape[0]:
rollx = maparray.shape[0] - center[0] - smallarray.shape[0]
center[0] = center[0] - abs(rollx )
if maparray.shape[1] - center[1] < smallarray.shape[1]:
rolly = maparray.shape[1] - center[1] - smallarray.shape[1]
center[1] = center[1] - abs(rolly )
maparray[center[0]:center[0]+smallarray.shape[0],center[1]:center[1]+smallarray.shape[1]] = smallarray
return np.roll(maparray, (abs(rollx ), abs(rolly )), axis=(0, 1))
print(imprint([7, 7], maparray, smallarray))
Output:
[[2. 0. 0. 0. 0. 0. 0. 2. 2.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0.]
[2. 0. 0. 0. 0. 0. 0. 2. 2.]
[2. 0. 0. 0. 0. 0. 0. 2. 2.]]
Given:
import numpy as np
maparray = np.zeros((9, 9))
smallarray = np.ones((3, 3)) * 2
We can simply assign to a slice:
# to assign with the top-left at (1, 2)
maparray[1:4, 2:5] = smallarray
which we can generalize:
def imprint(big, small, position):
y, x = position
h, w = small.shape
big[y:y+h, x:x+w] = small
If we need to wrap around the outside, then we can take a different approach, still without needing any conditional logic. Simply roll the big array such that the "imprinting" location is at the top-left; assign the values; then roll back. Thus:
def imprint(big, small, position):
y, x = position
h, w = small.shape
copy = np.roll(big, (-y, -x), (0, 1))
copy[:h, :w] = small
big[:, :] = np.roll(copy, (y, x), (0, 1))
Let's test it:
>>> imprint(maparray, smallarray, (7, 7))
>>> maparray
array([[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.]])
>>> imprint(maparray, smallarray, (2, 2))
>>> maparray
array([[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 2., 2., 2., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.],
[2., 0., 0., 0., 0., 0., 0., 2., 2.]])
We see that we can correctly leave the results from previous imprints in place, and also wrap around the edge.
It also works with negative indices and large indices, wrapping around in the expected manner:
>>> maparray[:] = 0 # continuing from before
>>> imprint(maparray, smallarray, (-1, -1))
>>> maparray
array([[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.]])
>>> # Keep in mind the first coordinate is for the first axis, which
>>> # displays vertically here (hence labelling it `y` in the code).
>>> imprint(maparray, smallarray, (40, 0))
>>> maparray
array([[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[2., 2., 2., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0.],
[2., 2., 0., 0., 0., 0., 0., 0., 2.]])
It will presumably be more efficient to use conditional logic explicitly (left as an exercise here), but this approach is elegant and easy to understand.
Related
I'm using the following code to generate an array based on coordinates of edges:
verts = np.array(list(itertools.product((0,2), (0,2))))
arr = np.zeros((5, 5))
arr[tuple(verts.T)] = 1
plt.imshow(arr)
which gives me
or, as a numeric array:
[[1., 0., 1., 0., 0.],
[0., 0., 0., 0., 0.],
[1., 0., 1., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]]
Now, I would like to fill out the spaces in between the corners (ie. yellow squares):
so that I get the following array:
[[1., 1., 1., 0., 0.],
[1., 1., 1., 0., 0.],
[1., 1., 1., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]]
Replace (0,2) using range(0,3) (3 as ranges are inclusive-exclusive) that is
import itertools
import numpy as np
verts = np.array(list(itertools.product(range(0,3), range(0,3))))
arr = np.zeros((5, 5))
arr[tuple(verts.T)] = 1
print(arr)
output
[[1. 1. 1. 0. 0.]
[1. 1. 1. 0. 0.]
[1. 1. 1. 0. 0.]
[0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0.]]
I have an array:
import numpy as np
arr = np.zeros(15)
And a pattern [1,2,3] that needs to be learned.
Meaning, I'd like the array to have this pattern.
The difficulty is learning a function that outputs this pattern, or as close as possible, given any pattern or array.
Currently I have the function hard coded:
def rule(array, item_idx):
try:
left_nbr = array[item_idx-1]
right_nbr = array[item_idx+1]
if left_nbr == 0 and right_nbr == 0:
array[item_idx] = 1
if arr[item_idx] == 0 and left_nbr == 1:
array[item_idx] = 2
if arr[item_idx] == 0 and left_nbr == 2:
array[item_idx] = 3
if arr[item_idx] == 0 and left_nbr == 3:
array[item_idx] = 1
except IndexError:
pass
return array
for i in range (0,len(arr)):
arr = rule(arr, i)
print(arr)
>>>
[1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 0. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 0. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 0. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 0. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 3. 0. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 0. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 0.]
[1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 3. 1. 2. 0.]
Is there a way to have a machine learning model that learns the rules (i.e. replace the function rule) to output the pattern? That is, instead of me hard coding the rules.
Update 1:
I tried creating a model using PyTorch but it doesn't seem to be able to learn the pattern (the loss doesn't decrease at all):
import torch
import torch.nn as nn
pattern = torch.tensor([1,2,3,1,2,3,1,2,3,1])
def custom_loss(output, target):
loss = torch.tensor(target - output).sum()
loss.requires_grad = True
return loss
model = torch.nn.Sequential(
torch.nn.Linear(3,1)
)
learning_rate = 1e-3
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)
for t in range(2000):
x = torch.zeros(10)
for i in range (0,len(pattern)):
try:
left_nbr = x[i-1]
center = x[i]
right_nbr = x[i+1]
output = model(torch.tensor([left_nbr, center, right_nbr]))
x[i] = int(output)
except IndexError:
pass
loss = custom_loss(x, pattern)
if t % 100 == 0:
print(t, loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
Problem
I observed that the prediction has never changed.
output = model(torch.tensor([left_nbr, center, right_nbr]))
x[i] = int(output)
...
loss = custom_loss(x, pattern)
I observed that output < 1, and thus, int(output) == 0.
So, it seems like that your target to compute loss against is always [0, ..., 0].
loss will never change because predictions are always the same.
Additionally, I am not sure what you want to do, however, it would be better to use your model's output as input for a loss function.
Quick Fix
Deep learning is good for classification, rather than regression, based on its ability to deeply extract features.
Check this how model predicts numbers written in images. It does not predict a real number of the range [0, 9], instead, it predicts fixed number of real numbers of the range [0, 1], thus, something like [0.1, ..., 0.9]. The position itself of the highest number is the number, thus class.
So, try the following code. This is how we usually use PyTorch.
data = [
([1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 1., 0.]),
([1., 2., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 1.]),
([1., 2., 3., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [1., 0., 0.]),
([1., 2., 3., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 1., 0.]),
([1., 2., 3., 1., 2., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 1.]),
([1., 2., 3., 1., 2., 3., 0., 0., 0., 0., 0., 0., 0., 0., 0.], [1., 0., 0.]),
([1., 2., 3., 1., 2., 3., 1., 0., 0., 0., 0., 0., 0., 0., 0.], [0., 1., 0.]),
([1., 2., 3., 1., 2., 3., 1., 2., 0., 0., 0., 0., 0., 0., 0.], [0., 0., 1.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 0., 0., 0., 0., 0., 0.], [1., 0., 0.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 0., 0., 0., 0., 0.], [0., 1., 0.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 2., 0., 0., 0., 0.], [0., 0., 1.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 2., 3., 0., 0., 0.], [1., 0., 0.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 0., 0.], [0., 1., 0.]),
([1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 2., 3., 1., 2., 0.], [0., 0., 1.]),
]
if __name__ == '__main__':
model = torch.nn.Sequential(
torch.nn.Linear(15, 30),
nn.Sigmoid(),
torch.nn.Linear(30, 10),
nn.Sigmoid(),
torch.nn.Linear(10, 3),
nn.Sigmoid()
)
learning_rate = 1e-3
optimizer = torch.optim.RMSprop(model.parameters(), lr=learning_rate)
mse = nn.MSELoss()
for t in range(10000):
X, y = random.choice(data)
output = model(torch.tensor(X, requires_grad=False))
loss = mse(output, torch.tensor(y, requires_grad=False))
if t % 100 == 0:
print(t, loss.item())
optimizer.zero_grad()
loss.backward()
optimizer.step()
Better Fix
Since your input is a sequence, you would better use Recurrent Neural Network (RNN).
I am trying multiply a specific location of an array by certain value, where the location is determined by the index and the value of the num array. The certain value comes from the same index position of the multiplier array. We only want to apply this multiplier if the needs_multiplier is value at that index position is true. I think the code will do a better job explaining this. I am trying to vectorize this and avoid the for loop.
import numpy as np
data = np.array([[[ 2., 2., 2., 2.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]],
[[ 1., 1., 1., 1.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]],
[[ 3., 3., 3., 3.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]],
[[ 5., 5., 5., 5.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]]])
needs_multiplier = np.array([True, True, False, True])
num = np.array([1, 2, 2, 3])
multipler = np.array([0.5, 0.6, 0.2, 0.3])
for i, cfn in enumerate(num):
if needs_multiplier[i]:
data[i, 1, cfn] = multipler[i] * data[i, 0, cfn]
data[i, 2, cfn] = data[i, 0, cfn]-data[i, 1, cfn]
print(data) # this is the result I am looking for
[[[2. 2. 2. 2. ]
[0. 1. 0. 0. ]
[0. 1. 0. 0. ]
[0. 0. 0. 0. ]]
[[1. 1. 1. 1. ]
[0. 0. 0.6 0. ]
[0. 0. 0.4 0. ]
[0. 0. 0. 0. ]]
[[3. 3. 3. 3. ]
[0. 0. 0. 0. ]
[0. 0. 0. 0. ]
[0. 0. 0. 0. ]]
[[5. 5. 5. 5. ]
[0. 0. 0. 1.5]
[0. 0. 0. 3.5]
[0. 0. 0. 0. ]]]
num can be used as index array after selecting "active" values with num[needs_multiplier]
Then vectorizing the expressions is pretty straight forward:
b = needs_multiplier
num_b = num[needs_multiplier]
data[b, 1, num_b] = multipler[b] * data[b, 0, num_b]
data[b, 2, num_b] = data[b, 0, num_b] - data[b, 1, num_b]
This question already has answers here:
Numpy extract submatrix
(6 answers)
How to create a sub-matrix in numpy
(2 answers)
Closed 2 years ago.
I'm relatively new to Python and would appreciate your help!
Suppose I have two square matrices - one large M and one smaller K - and an integer array of indices ind, not necessarily sorted. The length of ind matches the dimensions of K. In Octave/MATLAB, I can easily do this:
M(ind, ind) = K
This will distribute all the components of K to those positions of M that correspond to indices ind. This is often used in Finite Element computations.
Is there a way to do the same thing just as elegantly in Python? You may assume my M and K are NumPy arrays that were constructed via the operations:
M = np.zeros((12, 12))
K = np.zeros((6, 6))
I did some work on these matrices and filled them with data. My ind array is a NumPy array as well.
However, when I do something like
M[ind, ind] = K
I get shape mismatch as an error. Plugging ind.tolist() instead of ind into M won't change anything.
Thanks for any advice!
You are looking for this:
M[np.ix_(ind,ind)] = K
example:
M = np.zeros((12, 12))
K = np.ones((6, 6))
ind = np.arange(6)
output:
[[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[1. 1. 1. 1. 1. 1. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]]
If you are using numpy arrays, It can be donde directly using this syntax:
import numpy as np
M = np.zeros((12, 12))
K = np.ones((6, 6))
M[:6, :6] = K
# This is equivalent to:
# M[0:6, 0:6] = K
M will then look like this:
array([[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
Here you have more information about slice indexing in python
https://www.pythoncentral.io/how-to-slice-listsarrays-and-tuples-in-python/
This might be a super easy question if you know how to do it, but I just can't figure out the syntax:
I have an array of 5x10 zeros: y1 = np.zeros((5,10)) and an array 5x1 of index: index=np.array([2,3,2,5,6]). For each row of y1, I would like to set 1 at the column given by the index. The result would look like
array([[ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 1., 0., 0., 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]])
Anyone can help please :-) ?
You can do multi-dimensional array indexing with array[index_1, index_2]. For your problem:
y1[range(y1.shape[0]), index] = 1
range(y1.shape[0]) generates the array [0,1,...,n-1], where n is the number of rows in y1. This array is your row index, and index is your column index.
Just use an enumerate()
import numpy as np
y1 = np.zeros((5,10))
index=np.array([2,3,2,5,6])
for i,item in enumerate(y1):
item[index[i]] = 1
print(y1)
# [[ 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
# [ 0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]]
Does this do what you want?