-1

I am trying to subset a matrix by using values from another smaller matrix. The number of rows in each are the same, but the smaller matrix has fewer columns. Each column in the smaller matrix contains the value of the column in the larger matrix that should be referenced. Here is what I have done, along with comments that hopefully describe this better, along with what I have tried. (The wrinkle in this is that the values of the columns to be used in each row change...) I have tried Google, searching on stackoverflow, etc and can't find what I'm looking for. (The closest I came was something in sage called matrix_from_columns, which isn't being used here) So I'm probably making a very simple referencing error. TIA, mconsidine

import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view

#Problem:  for each row in a matrix/image I need to replace
#          a value in a particular column in that row by a
#          weighted average of some of the values on either
#          side of that column in that row.  The wrinkle
#          is that the column that needs to be changed may
#          vary from row to row.  The columns that need to
#          have their values changes is stored in an array.
#
#          How do I do something like:
#           img[:, selectedcolumnarray] = somefunction(img,targetcolumnmatrix)
#
#          I can do this for setting the selectedcolumnarray to a value, like 0
#          But I am not figuring out how to select the targeted values to
#          average.

#dimensions of subset of the matrix/image that will be averaged
rows = 7
columns = 5

#weights that will be used to average surrounding values
the_weights = np.ones((rows,columns)).astype(float)*(1/columns)
print(the_weights)

#make up some data to create a set of column
# values that vary by row
y = np.asarray(range(0,rows)).astype(float)
x = -0.095*(y**2) - 0.05*y + 12.123
fit=[x.astype(int),x-x.astype(int),y]
print(np.asarray(fit)[0])

#create a test array, eg "image' of 20 columns that will have
# values in targeted columns replaced
testarray = np.asarray(range(1,21))
img = np.ones((rows,20)).astype(np.uint16)
img = img*testarray.T #give it some values
print(img)

#values of the rows that will be replaced
targetcolumn = np.asarray(fit)[0].astype(int)
print(targetcolumn)

#calculate the range of columns in each row that
#  will be used in the averaging
startcol = targetcolumn-2
endcol = targetcolumn+2
testcoords=np.linspace(startcol,endcol,5).astype(int).T
#this is the correct set of columns in the corresponding
#  row to use for averaging
print(testcoords)

img2=img.copy()
#this correctly replaces the targetcolumn values with 0
#  but I want to replace them with the sum of the values
#  in the respective row of testcoords, weighted by the_weights
img2[np.arange(rows),targetcolumn]=0

#so instead of selecting the one column, I want to select
# the block of the image represented by testcoords, calculate
# a weighted average for each row, and use those values instead
# of 0 to set the values in targetcolumn

#starting again with the 7x20 (rowsxcolumns) "image"
img3=img.copy()
#this gives me the wrong size, ie 7,7,5 when I think I want 7,5;
print(testcoords.shape)

#I thought "take" might help, but ... nope
#img3=np.take(img,testcoords,axis=1)

#something here maybe??? :
#https://stackoverflow.com/questions/40084931/taking-subarrays-from-numpy-array-with-given-stride-stepsize
# but I can't figure out what


##### plot surface to try to visualize what is going on ####
'''
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})

# Make data.
X = np.arange(0, 20, 1)
Y = np.arange(0, rows, 1)
X, Y = np.meshgrid(X, Y)
Z = img2

# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False)

# Customize the z axis.
ax.set_zlim(0, 20)
ax.zaxis.set_major_locator(LinearLocator(10))
# A StrMethodFormatter is used automatically
ax.zaxis.set_major_formatter('{x:.02f}')

# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
mconsidine
  • 53
  • 3
  • Have you checked the [documentation](https://numpy.org/devdocs/user/basics.indexing.html)? There's a good reference for advanced indexing techniques. – ddejohn Feb 04 '22 at 23:26
  • Please provide a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). – ddejohn Feb 04 '22 at 23:28
  • Yes, I looked at that. And that example is reproducible, ie it runs as-is – mconsidine Feb 05 '22 at 00:00
  • Reproducible? Yes. Minimal? Absolutely not. Sorry, but asking people to read through all that code, and all those comments isn't particularly courteous to the people volunteering their time to help you. You need to provide a more concise problem statement, with a sample input and expected output. – ddejohn Feb 05 '22 at 00:02
  • Do not edit "Solved" into your question title. Clicking the checkbox by an answer is the _only_ correct way to mark a question solved. (Yes, there's a delay before you can accept your own answer; the idea is to leave the question open long enough to consider and evaluate any other answers that are proposed). – Charles Duffy Feb 05 '22 at 00:04
  • Apologies for the "solved" edit. And ditto for the this not being considered minimal. – mconsidine Feb 05 '22 at 00:05
  • PS I can't use the check mark for two days, for some reason... – mconsidine Feb 05 '22 at 00:05

1 Answers1

0

It turns out that "take_along_axis" does the trick:

imgsubset = np.take_along_axis(img3,testcoords,axis=1)
print(imgsubset)
newvalues = imgsubset * the_weights
print(newvalues)
newvalues = np.sum(newvalues, axis=1)
print(newvalues)
img3[np.arange(rows),targetcolumn] = np.round(newvalues,0)
print(img3)

(It becomes more obvious when non trivial weights are used.)

Thanks for listening... mconsidine

mconsidine
  • 53
  • 3