Reshaping, Flattening, or Raveling a Multidimensional Array within a Class - python

I am somewhat of a beginner when it comes to using Matplotlib and Python in general. I am trying to create a class that can generate multiple subplots given basic information such as how many rows and columns the user wants. I am running into an issue when trying to convert the multidimensional array that comes from matplotlib.pyplot.subplots(). Here is a copy of what I am working with currently:
import math
import numpy as np
import matplotlib.pyplot as plt
class subplotgrid:
def __init__(self, rows, cols, width, height): # class initialization
self.rows = rows
self.cols = cols
self.width = width
self.height = height
self.pltarray = [] # refers to the subplot number, values are 0 - (rows x cols)
def create(self, h_spacing = 0.2, w_spacing =0.1): # generate the subplots,
# h_spcing and v_spacing are optional params with default values
fig, pltarray = plt.subplots(self.rows, self.cols, figsize=(rows*self.width, cols*self.height)) # Creates subplots, rows x cols, total figure size
fig.subplots_adjust(hspace = .2, wspace=.1) # Specifies spaces between each subplot
self.pltarray = self.pltarray.reshape(-1) # axes within plt.subplot() are multidimensional arrays that can't be iterated over, so we have to flatten it with .ravel()
def show_scatter(self, x_array, y_array, dotSize = 0.5, title = "DEFAULT TITLE"):
for i in range(0, rows+cols-1): # loops through all of the subplots
self.pltarray[i].scatter(x[:,0],x[:,1], s=dotSize, c='red') # Scatter plot all points red
self.pltarray[i].scatter(inside[:,0],inside[:,1],s=dotSize, c='blue') # Scatter plot points inside blue
if (isinstance(title, np.ndarray)): # check if we are passing an array of titles, test passes True if we are
pltarray[i].set_title(title[i]) # set the title to the indexed title for the title array
else:
axs[i].set_title(title) # set the title to a static value if no title array was found
plt.show()
however the line self.pltarray = self.reshape(-1) throws the error:
AttributeError: 'subplotgrid' object has no attribute 'reshape'
with similar issues for .ravel() and .flatten() respectively. Why does this error show up and how does one go about fixing it?

You are reshaping your own class self.reahape(-1) which has no reshape method implemented. You need to reshape the axis array:
self.pltarray = pltarray.reshape(-1)

Related

Selecting a subset of columns in a matrix using values stored in another matrix in Python

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()
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

Make each Y value to correspond to an X value in pyqtgraph

I have an algorithm which takes some data and plots them in real time. I have a pyqt5 window and I use pyqtgraph to plot on the window. The code snippet I use is below;
import pyqtgraph as pg
from PyQt5.QtWidgets import QMainWindow
from Ui_GraphicsLayout import Ui_GraphicsLayout
class TimeDomainPlotWindow(QMainWindow):
closing = pyqtSignal()
def __init__(self, title = "Time Domain Plot", name = "Channel"):
super().__init__()
pg.setConfigOption('background', 'w')
self.__ui = Ui_GraphicsLayout()
self.__ui.setupUi(self)
self.setWindowTitle("Real Time Data - {:s}".format(title))
self.__plot = self.__ui.widget.addPlot(title = name, row = 0, col = 0)
self.__pditem = self.__plot.plot(pen = 'k')
def plot(self, data):
self.__pditem.setData(data)
The plot I get looks like this:
So I send an array of 1000 values and it plots them. Because I have 1000 values in my array it numbers the X axis from 0 to 1000. I want to change the range of values in X axis and make each value in my data array correspond to an X value in the graph. So just like in excel where you have two columns of data and one of them is your X axis and the other is your Y axis. I want to, lets say have an X axis that has values from 1 to 100 but just have 10 Y values and make each of those 10 values be a point at a specific X axis value. Is it possible to do this?
The setData function has an optional x argument which does what you're describing, so your plot function would become:
def plot(self, data, x_list):
self.__pditem.setData(y=data, x=x_list)
where x_list is a list with the same length as data.
To change the x-axis to 1 to 100 you'd use the setXRange function, see this documentation

saving a grid of heterogenous images in Python

How can I use something like below line to save images on a grid of 4x4 of heterogenous images? Imagine that images are identified by sample[i] and i takes 16 different values.
scipy.misc.imsave(str(img_index) + '.png', sample[1])
Similar to this answer but for 16 different images
https://stackoverflow.com/a/42041135/2414957
I am not biased towards the used method as long as it does the deed. Also, I am interested in saving images rather than showing them using plt.show() as I am using a remote server and dealing with CelebA image dataset which is a giant dataset. I just want to randomly select 16 images from my batch and save the results of DCGAN and see if it makes any sense or if it converges.
*Currently, I am saving images like below:
batch_no = random.randint(0, 63)
scipy.misc.imsave('sample_gan_images/iter_%d_epoch_%d_sample_%d.png' %(itr, epoch, batch_no), sample[batch_no])
and here, I have 25 epochs and 2000 iterations and batch size is 64.
Personally, I tend to use matplotlib.pyplot.subplots for these kinds of situations. If your images are really heterogenous it might be a better choice than the image concatenation based approach in the answer you linked to.
import matplotlib.pyplot as plt
from scipy.misc import face
x = 4
y = 4
fig,axarr = plt.subplots(x,y)
ims = [face() for i in range(x*y)]
for ax,im in zip(axarr.ravel(), ims):
ax.imshow(im)
fig.savefig('faces.png')
My big complaint about subplots is the quantity of whitespace in the resulting figure. As well, for your application you may not want the axes ticks/frames. Here's a wrapper function that deals with those issues:
import matplotlib.pyplot as plt
def savegrid(ims, rows=None, cols=None, fill=True, showax=False):
if rows is None != cols is None:
raise ValueError("Set either both rows and cols or neither.")
if rows is None:
rows = len(ims)
cols = 1
gridspec_kw = {'wspace': 0, 'hspace': 0} if fill else {}
fig,axarr = plt.subplots(rows, cols, gridspec_kw=gridspec_kw)
if fill:
bleed = 0
fig.subplots_adjust(left=bleed, bottom=bleed, right=(1 - bleed), top=(1 - bleed))
for ax,im in zip(axarr.ravel(), ims):
ax.imshow(im)
if not showax:
ax.set_axis_off()
kwargs = {'pad_inches': .01} if fill else {}
fig.savefig('faces.png', **kwargs)
Running savegrid(ims, 4, 4) on the same set of images as used earlier yields:
If you use savegrid, if you want each individual image to take up less space, pass the fill=False keyword arg. If you want to show the axes ticks/frames, pass showax=True.
I found this on github, also sharing it:
import matplotlib.pyplot as plt
def merge_images(image_batch, size):
h,w = image_batch.shape[1], image_batch.shape[2]
c = image_batch.shape[3]
img = np.zeros((int(h*size[0]), w*size[1], c))
for idx, im in enumerate(image_batch):
i = idx % size[1]
j = idx // size[1]
img[j*h:j*h+h, i*w:i*w+w,:] = im
return img
im_merged = merge_images(sample, [8,8])
plt.imsave('sample_gan_images/im_merged.png', im_merged )

Python: using X and Y values to draw a picture

I have a series of methods that take an image 89x22 pixels (although the size, theoretically, is irrelevant) and fits a curve to each row of pixels to find the location of the most significant signal. At the end, I have a list of Y-values, one for each row of pixels, and a list of X-values, the location of the most significant peak for each row.
I would like to test different types of curves to see which models the data better, and in order to do so, I would like to be able to print out a new image, also 89x22 pixels, with the location of the most significant peak marked with a single red pixel for each line. A have attached an input example and a (poorly drawn) example of what I expect a good output to look like:
Any suggestions on which modules to start looking in?
class image :
def importImage (self) :
"""Open an image and sort all pixel values into a list of lists"""
from PIL import Image #imports Image from PIL library
im = Image.open("testTop.tif") #open the file
size = im.size #size object is a tuple with the pixel width and pixel height
width = size[0] #defines width object as the image width in pixels
height = size[1] #defines the height object as the image height in pixels
allPixels = list(im.getdata()) #makes a list of all pixels values
pixelList = [allPixels[width*i : width * (i+1)] for i in range(height)] #takes mega-list and makes a list of lists by row
return(pixelList) #returns list of lists
def fitCurves (self) :
"""
Iterate through a list of lists and fit a curve to each list of integers.
Append the position of the list and the location of the vertex to a growing list.
"""
from scipy.optimize import curve_fit
import numpy as np
from matplotlib import pyplot as pp
from scipy.misc import factorial
image = self.importImage()
xList = []
yList = []
position = 0
for row in image :
#Gaussian fit equations kindly provided by user mcwitt
x = np.arange(len(row))
ffunc = lambda x, a, x0, s: a*np.exp(-0.5*(x-x0)**2/s**2) # define function to fit
p, _ = curve_fit(ffunc, x, row, p0=[100,5,2]) # fit with initial guess a=100, x0=5, s=2
x0 = p[1]
yList.append(position)
position = position + 1
xList.append(x0)
print(yList)
print(xList)
newImage = image()
newImage.fitCurves()
Mabye:
import numpy as np
from matplotlib import pyplot as plt
from scipy import ndimage
from scipy import optimize
%matplotlib inline
# just a gaussian (copy paste from lmfit, another great package)
def my_gaussian(p,x):
amp = p[0]
cen = p[1]
wid = p[2]
return amp * np.exp(-(x-cen)**2 /wid)
# I do like to write a cost function separately. For the leastsquare algorithm it should return a vector.
def my_cost(p,data):
return data - my_gaussian(p,data)
# i load the image and generate the x values
image = ndimage.imread('2d_gaussian.png',flatten=True)
x = np.arange(image.shape[1])
popt = []
# enumerate is a convenient way to loop over an iterable and keep track of the index.
y = []
for index,data in enumerate(image):
''' this is the trick to make the algorithm robust.
I do plug the index of the maximum value of the current row as
initial guess for the center. Maybe it would be enough to do
just that and the fit is unnecessary. Haven`t checked that.
'''
max_index = np.argmax(data)
# initial guess.
x0 = [1.,max_index,10]
# call to the solver
p,_ = optimize.leastsq(my_cost, x0, args = data)
popt.append(p)
y.append(index)
'''
I do transpose the data.
As a consequence the values are stored row, not columnwise.
It is often easier to store the reusults inside a loop and
convert the data later into a numpy array.
'''
gaussian_hat = np.array(popt).T
# without the transpose, it would be center = gaussian_hat[:,1]
center = gaussian_hat[1]
y = np.array(y)
''' i do like to use an axis handle for the plot.
Not necessary, but gives me the opportunity to add new axis if necessary.
'''
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.imshow(image)
# since it is just a plot, I can plot the x, y coordinates
ax.plot(center,y,'k-')
# fitt of a 3th order polynomial
poly = np.polyfit(y,center,3)
# evaluation at points y
x_hat = np.polyval(poly,y)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.imshow(image)
ax.plot(x_hat,y,'k-')
plt.savefig('2d_gaussian_fit.png')

Delete image frame from 3d array

I am a new Python user and would like to do some simple image processing. Essentially I will have a dynamic medical image - a series of 2D images at different time points which I would like to store as a 3D array. Due to the nature of the scanning technique there is likely to be occasional patient motion during certain imaging frames which makes the data unusable. I would like to delete such frames and recast the array - new dimensions (n-1, 256, 256). After deletion of the frame I would like to update the image display. What is the best way to achieve this goal? Here is the skeleton code I have so far:
import dicom
import numpy as np
import pylab
from matplotlib.widgets import Slider, Button
ds = dicom.read_file("/home/moadeep/Dropbox/FS1.dcm")
#data = ds.pixel_array
data = np.random.rand(16,256,256)
nframes = data.shape[0]
ax = pylab.subplot(111)
pylab.subplots_adjust(left=0.25, bottom=0.25)
frame = 0
l = pylab.imshow(data[frame,:,:]) #shows 1024x256 imagge, i.e. 0th frame*
axcolor = 'lightgoldenrodyellow'
axframe = pylab.axes([0.35, 0.1, 0.5, 0.03], axisbg=axcolor)
#add slider to scroll image frames
sframe = Slider(axframe, 'Frame', 0, nframes, valinit=0,valfmt='%1d'+'/'+str(nframes))
ax_delete = pylab.axes([0.8,0.025,0.1,0.04], axisbg=axcolor)
#add slider to scroll image frames
#Delete button to delete frame from data set
bDelete = Button(ax_delete, 'Delete')
def update(val):
frame = np.around(sframe.val)
pylab.subplot(111)
pylab.subplots_adjust(left=0.25, bottom=0.25)
pylab.imshow(data[frame,:,:])
sframe.on_changed(update)
pylab.gray()
pylab.show()
The short answer to your question is use numpy.delete. E.g.
import numpy as np
data = np.arange(1000).reshape((10,10,10))
# Delete the third slice along the first axis
# (note that you can delete multiple slices at once)
data = np.delete(data, [2], axis=0)
print data.shape
However, this is a poor approach if you're going to be removing individual slices many times.
The longer answer is to avoid doing this each time you want to delete a slice.
Numpy arrays have to be contiguous in memory. Therefore, this will make a new copy (and delete the old) each time. This will be relatively slow, and requires you to have twice the free memory space required to store the array.
In your case, why not store a python list of 2D arrays? That way you can pop the slices you don't want out without any problems. If you need it as a 3D array afterwards, just use numpy.dstack to create it.
Of course, if you need to do 3D processing, you'll need the 3D array. Therefore, another approach would be to store a list of "bad" indicies and remove them at the end using numpy.delete (note that the items to be deleted is a list, so you can just pass in your list of "bad" indicies).
On a side note, the way you're updating the image will be very slow.
You're creating lots of images, so each one will be redrawn each time and the update will become very slow as you go on.
You're better off setting the data of the image (im.set_data(next_slice)) instead of creating a new image each time.
Better yet, use blitting, but with image data in matplotlib, it's not as advantageous as it is for other types of plots due to matplotlib's slow-ish rescaling of images.
As a quick example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
def main():
# Set up 3D coordinates from -10 to 10 over a 200x100x100 "open" grid
x, y, z = np.ogrid[-10:10:200j, -10:10:100j, -10:10:100j]
# Generate a cube of interesting data
data= np.sin(x*y*z) / (x*y*z)
# Visualize it
viewer = VolumeViewer(data)
viewer.show()
class VolumeViewer(object):
def __init__(self, data):
self.data = data
self.nframes = self.data.shape[0]
# Setup the axes.
self.fig, self.ax = plt.subplots()
self.slider_ax = self.fig.add_axes([0.2, 0.03, 0.65, 0.03])
# Make the slider
self.slider = Slider(self.slider_ax, 'Frame', 1, self.nframes,
valinit=1, valfmt='%1d/{}'.format(self.nframes))
self.slider.on_changed(self.update)
# Plot the first slice of the image
self.im = self.ax.imshow(data[0,:,:])
def update(self, value):
frame = int(np.round(value - 1))
# Update the image data
dat = self.data[frame,:,:]
self.im.set_data(dat)
# Reset the image scaling bounds (this may not be necessary for you)
self.im.set_clim([dat.min(), dat.max()])
# Redraw the plot
self.fig.canvas.draw()
def show(self):
plt.show()
if __name__ == '__main__':
main()

Categories