Creating a colourmap plot in pyplot with random data and positions - python

I want to plot a map (let's call it testmap) of shape (100,3) with a colourmap. Each row consists of the x-position, y-position and data, all randomly drawn.
map_pos_x = np.random.randint(100, size=100)
map_pos_y = np.random.randint(100, size=100)
map_pos = np.stack((map_pos_x, map_pos_y), axis=-1)
draw = np.random.random(100)
draw = np.reshape(draw, (100,1))
testmap = np.hstack((map_pos, draw))
I do not want to use a scatterplot, since the map positions are supposed to emulate pixels of a camera. If I try something like
plt.matshow(A=testmap)
I get a 100*2 map. However, I want a 100*100 map. Positions with no data can be black. How can I do this?
edit: I have now adopted the following:
grid = np.zeros((100, 100))
i=0
for pixel in map_pos:
grid[pixel[0], pixel[1]] = draw[i]
i=i+1
This produces what I want to have. The reason why I do not draw the random numbers in the loop itself, but iterate over the existing array "draw", is that the numbers that are being drawn are first being operated on, so I want to have the freedom to manipulate "draw" independently of the loop.
This code also produces double entries/non-unique pairs, which is fine by itself, but I would like to identify these double pairs and add up "draw" for these pairs. How can I do that?

You can first create empty pixels, either with zeros (gets the "lowest" color) or NaNs (these pixels would be invisible). Then you can use numpy's smart indexing to fill in the values. For this to work, it is important that the map_pos_x and map_pos_y are integer coordinates in the correct range.
import numpy as np
import matplotlib.pyplot as plt
map_pos_x = np.random.randint(100, size=100)
map_pos_y = np.random.randint(100, size=100)
draw = np.random.random(100)
# testmap = np.full((100,100), np.nan)
testmap = np.zeros((100,100))
testmap[map_pos_x, map_pos_y] = draw
plt.matshow(testmap)
plt.show()
PS: About your new question, to count how many xy positions coincide, np.histogram2d could be used. The result can also be plotting via matshow. A benefit is that the xy values don't need to be integers: they will be summed depending on their rounded values.
If every xy position also has a value, such as the array draw in the question, it can be passed as np.histogram2d(...., weights=draw).
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(1234)
N = 100
map_pos_x = np.random.randint(N, size=10000)
map_pos_y = np.random.randint(N, size=len(map_pos_x))
fig, (ax1, ax2) = plt.subplots(ncols=2)
testmap1, xedges, yedges = np.histogram2d(map_pos_x, map_pos_y, bins=N, range=[[0, N - 1], [0, N - 1]])
ax1.matshow(testmap1)
plt.show()
To show what happens, here is a test with N=10 with the matshow at the left. At right there is a scatter plot with semitransparent dots, making them darker when there are more dots coinciding.

a solution is this:
import numpy as np
import matplotlib.pyplot as plt
import random
import itertools
#gets as input the size of the axis and the number of pairs
def get_random_pairs(axis_range, count):
numbers = [i for i in range(0,axis_range)]
pairs = list(itertools.product(numbers,repeat=2))
return random.choices(pairs, k=count)
object_positions = get_random_pairs(100,100)
grid = np.zeros((100, 100))
for pixel in object_positions:
grid[pixel[0],pixel[1]] = np.random.random()
print(pixel)
plt.matshow(A=grid)
result:
edit:
since the grid is initialized to zero then just add the new value to the old one
n_pixels_x = 100
n_pixels_y = 100
map_pos_x = np.random.randint(100, size=100)
map_pos_y = np.random.randint(100, size=100)
map_pos = np.stack((map_pos_x, map_pos_y), axis=-1)
draw = np.random.random(100)
draw = np.reshape(draw, (100,1))
testmap = np.hstack((map_pos, draw))
grid = np.zeros((n_pixels_x, n_pixels_y))
for pixel in map_pos:
grid[pixel[0], pixel[1]] = grid[pixel[0], pixel[1]] + draw[i]
plt.matshow(A=grid)

Related

How to plot lines between points, and change their color based on specific values in Python?

Context:
3x35 values array that associates 1 value per segment
4x35x2 matpos array that gathers the coordinates of 4x35 points (hence 3x35 segments).
Question:
How can I define each segment's color based on their values from the values array ?
Code attempt:
# Array of values for each point
values = np.random.rand(3,35)
# Generate array of positions
x = np.arange(0,35)
y = np.arange(0,4)
matpos = np.array([[(y[i], x[j]) for j in range(0,len(x))] for i in range(0,len(y))])
# plot the figure
plt.figure()
for i in range(len(y)-1):
for j in range(len(x)):
# plot each segment
plt.plot(matpos[i:i+2,j,0],matpos[i:i+2,j,1]) #color = values[i,j]
If your values are just along a grid, you might as well just use plt.imshow(values).
Updated code for desired result:
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# Array of values for each point
values = np.random.rand(3,35)
# Transform value to colors depending on colormap
color_norm = mpl.colors.Normalize(np.min(values), np.max(values))
color_map = mpl.cm.get_cmap('viridis')
colors = color_map(color_norm(values))
plt.close('all')
plt.figure()
for (y, x), value in np.ndenumerate(values):
plt.plot([x, x+1], [y, y], c = colors[y,x], linewidth = 10)

How do I specify the number of axis points in matplotlib and how do I extract theese points?

I have a small script that creates a matplotlib graph with 2000 random points following a random walk.
I'm wondering if there is a simple way to change the number of points on the y-axis as well as how I can extract these values?
When I run the code below, I get 5 points on the Y-axis but I'm looking for a way to expand this to 20 points as well as creating an array or series with these values. Many thanks in advance.
import matplotlib.pyplot as plt
dims = 1
step_n = 2000
step_set = [-1, 0, 1]
origin = np.zeros((1,dims))
random.seed(30)
step_shape = (step_n,dims)
steps = np.random.choice(a=step_set, size=step_shape)
path = np.concatenate([origin, steps]).cumsum(0)
plt.plot(path)
import matplotlib.pyplot as plt
import numpy as np
import random
dims = 1
step_n = 2000
step_set = [-1, 0, 1]
origin = np.zeros((1,dims))
random.seed(30)
step_shape = (step_n,dims)
steps = np.random.choice(a=step_set, size=step_shape)
path = np.concatenate([origin, steps]).cumsum(0)
#first variant
plt.plot(path)
plt.locator_params(axis='x', nbins=20)
plt.locator_params(axis='y', nbins=20)
You can use locator_params in order to specify the number of ticks. Of course you can retrieve these points. For this you must create a subplot with ax, and then you can get the y_ticks with get_yticks.
#second variant
# create subplot
fig, ax = plt.subplots(1,1, figsize=(20, 11))
img = ax.plot(path)
plt.locator_params(axis='y', nbins=20)
y_values = ax.get_yticks() # y_values is a numpy array with your y values

Python SVD fix the number of eigenvalues to rebuild the image?

I am trying to rebuild an image that I previously decomposed with SVD. The image is this:
I successfully decomposed the image with this code:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
s an array of the singular values of the image. The more singular values I take, the more the reconstructed image is similar to the original one.
For example, if I take 20 singular values:
n = 20
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
plt.show()
I would like to fix the minumum number of singular values in order to get a good result: an image pretty similary to the original one. Moreover, I would like to see how much the result changes when I take a higher number of singular values. I tried with an animation without success:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig = plt.figure()
def update(i):
S = np.zeros(np.shape(img))
n = 20
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
plt.imshow(recon_img)
plt.axis('off')
ani = FuncAnimation(fig = fig, func = update, frames = 20, interval = 10)
plt.show()
If you plot the s singular values you can see a very steep decreasing curve, better if you use a log scale for the y axis:
plt.semilogy(s, 'k-')
As you can see, the first 50 singular values are the most important ones: almost everyone more that 1000. Values from the ~50th to the ~250th are an order of magnitude lower and their values decreases slowly: the slope of the curve is contained (remember the logarithmic y scale). That beeing said I would take the first 50 elements to rebulid your image.
Regarding the animation:
while the animation updates frame by frame, the counter i is increased by 1. In your code, you mistakenly use i to slice the s and define S; you should rename the counter.
Moreover, as animation goes on, you need to take an increasing number of singular values, this is set by n which you keep constant frame by frame. You need to update n at each loop, so you can use it as the counter.
Furthermore, you need the erase the previous plotted image, so you need to add a plt.gca().cla() at the beginning of the update function.
Check the code below for reference:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
img = Image.open('steve.jpg')
img = np.mean(img, 2)
U,s,V = np.linalg.svd(img)
fig, ax = plt.subplots(1, 2, figsize = (4, 4))
ax[0].imshow(img)
ax[0].axis('off')
ax[0].set_title('Original')
def init():
ax[1].cla()
ax[1].imshow(np.zeros(np.shape(img)))
ax[1].axis('off')
ax[1].set_title('Reconstructed\nn = 00')
def update(n):
ax[1].cla()
S = np.zeros(np.shape(img))
for i in range(0, n):
S[i, i] = s[i]
recon_img = U#S#V
ax[1].imshow(recon_img)
ax[1].axis('off')
ax[1].set_title(f'Reconstructed\nn = {n:02}')
ani = FuncAnimation(fig = fig, func = update, frames = 50, init_func = init, interval = 10)
ani.save('ani.gif', writer = 'imagemagick')
plt.show()
which gives this animation:
As you can see, the first 50 elements are enough to rebuild you image pretty well. The rest of the elements adds some noise and changes a little the background.

Defining a 2D object and using its area as Boolean

I have defined two space dimesions ( x and z ) and I was able to manually "draw" an object to use it as a boolen for solving an equation. I defined it as it follows:
A = np.zeros((nz,nx))
object = np.ones_like(A)
object[ int(5/dz):int(10/dz) , int(5/dx):int(10/dz) ] = 2
object = object == 2
By doing that I can define an square 5x10 in z dimesion and 5x10 in x dimesion , and apply the algorythim which understands this as an area , I think. But when it comes to draw complex areas it ends up being hard doing it by little squares and rectangles.
So I want to automatize an area generation by mouse clicking and I want to be able to use this area as a boolean.
I was able to draw a polygon using:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Polygon
fig, ax = plt.subplots()
object = np.array(plt.ginput(n=-100,mouse_stop=2))
p = Polygon(object, alpha=0.5)
plt.gca().add_artist(p)
plt.draw()
plt.show()
But this outputs z and x coordinates of the vertices, and I tried to use it as boleean but I could'nt write it so that python uderstands it as the area defined by those points.
Is this problem easy to solve?
If you just want to calculate the area of a general polygon, you can use for example the Shapely python package like this:
import numpy as np
import matplotlib.pyplot as plt
from shapely.ops import Polygon
from matplotlib.patches import Polygon as PltPolygon
# Get the coordinate input
canvas_size = np.array([1, 1])
canvas_lim = np.array([[0, canvas_size[0]], [0, canvas_size[1]]])
fig, ax = plt.subplots()
plt.xlim(canvas_lim[0])
plt.ylim(canvas_lim[1])
ax.set_aspect("equal")
coordinates = np.array(plt.ginput(n=-100, mouse_stop=2))
# Use shapely.ops.Polygon to calculate the area
poly = Polygon(coordinates)
area = poly.area
print("The area is {} units^2".format(area))
# Draw the polygon
p = PltPolygon(coordinates, alpha=0.5)
ax.add_artist(p)
plt.show()
If you definitely need the mask, here's one way to rasterize it using numpy and matplotlib.path. For details see the comments in the code:
import numpy as np
import matplotlib.path as mpltPath
import matplotlib.pyplot as plt
# Define the limits of our polygon
canvas_desired_size = np.array([110, 100])
# The pixel size with which we calculate (number of points to consider)
# The higher this number, the more we have to calculate, but the
# closer the approximation will be
pixel_size = 0.1
# Cacluate the actual size of the canvas
num_pxiels = np.ceil(canvas_desired_size / pixel_size).astype(int)
canvas_actual_size = num_pxiels * pixel_size
# Let's create a grid where each pixel's value is it's position in our 2d image
x_coords = np.linspace(
start=0,
stop=canvas_actual_size[0],
endpoint=False,
num=canvas_desired_size[0] / pixel_size,
)
y_coords = np.linspace(
start=0,
stop=canvas_actual_size[1],
endpoint=False,
num=canvas_desired_size[1] / pixel_size,
)
# Since it makes more sense to check if the middle of the pixel is in the
# polygion, we shift everything with half pixel size
pixel_offset = pixel_size / 2
x_centers = x_coords + pixel_offset
y_centers = y_coords + pixel_offset
xx, yy = np.meshgrid(x_centers, y_centers, indexing="ij")
# Flatten our xx and yy matrixes to an N * 2 array, which contains
# every point in our grid
pixel_centers = np.array(
list(zip(xx.flatten(), yy.flatten())), dtype=np.dtype("float64")
)
# Now prompt for the imput shape
canvas_lim = np.array([[0, canvas_actual_size[0]], [0, canvas_actual_size[1]]])
fig, ax = plt.subplots()
plt.xlim(canvas_lim[0])
plt.ylim(canvas_lim[1])
ax.set_aspect("equal")
shape_points = np.array(plt.ginput(n=-100, mouse_stop=2))
# Create a Path object
shape = mpltPath.Path(shape_points)
# Use Path.contains_points to calculate if each point is
# within our shape
shape_contains = shape.contains_points(pixel_centers)
# Reshape the result to be a matrix again
mask = np.reshape(shape_contains, num_pxiels)
# Calculate area
print(
"The shape area is roughly {} units^2".format(
np.sum(shape_contains) * pixel_size ** 2
)
)
# Show the rasterized shape to confirm it looks correct
plt.imshow(np.transpose(mask), aspect="equal", origin="lower")
plt.xlim([0, num_pxiels[0]])
plt.ylim([0, num_pxiels[1]])
plt.show()
Alternatively, a simpler solution would be using your plot as an image and thresholding it to get a boolean mask. There should be plent of examples of how to do this on google.

Heat map for a very large matrix, including NaNs

I am trying to see if NaNs are concentrated somewhere, or if there is any pattern for their distribution.
The idea is to use python to plot a heatMap of the matrix (which is 200K rows and 1k columns) and set a special color for NaN values (the rest of the values can be represented by the same color, this doesn't matter)
An example of a possible display:
Thank you all in advance
A 1:200 aspect ratio is pretty bad and, since you could run into memory issues, you should probably break it up into several Nx1k pieces.
That being said, here is my solution (inspired by your example image):
from mpl_toolkits.axes_grid1 import AxesGrid
# generate random matrix
xDim = 2000
yDim = 4000
# number of nans
nNans = xDim*yDim*.1
rands = np.random.rand(yDim, xDim)
# create a skewed distribution for the nans
x = np.clip(np.random.gamma(2, yDim*.125, size=nNans).astype(np.int),0 ,yDim-1)
y = np.random.randint(0,xDim,size=nNans)
rands[x,y] = np.nan
# find the nans:
isNan = np.isnan(rands)
fig = plt.figure()
# make axesgrid so we can put a histogram-like plot next to the data
grid = AxesGrid(fig, 111, nrows_ncols=(1, 2), axes_pad=0.05)
# plot the data using binary colormap
grid[0].imshow(isNan, cmap=cm.binary)
# plot the histogram
grid[1].plot(np.sum(isNan,axis=1), range(isNan.shape[0]))
# set ticks and limits, so the figure looks nice
grid[0].set_xticks([0,250,500,750,1000,1250,1500,1750])
grid[1].set_xticks([0,250,500,750])
grid[1].set_xlim([0,750])
grid.axes_llc.set_ylim([0, yDim])
plt.show()
Here is what it looks like:
# Learn about API authentication here: https://plot.ly/python/getting-started
# Find your api_key here: https://plot.ly/settings/api
import plotly.plotly as py
import plotly.graph_objs as go
data = [
go.Heatmap(
z=[[1, 20, 30],
[20, 1, 60],
[30, 60, 1]]
)
]
plot_url = py.plot(data, filename='basic-heatm
soruce: https://plot.ly/python/heatmaps/
What you could do is use a scatter plot:
import matplotlib.pyplot as plt
import numpy as np
# create a matrix with random numbers
A = np.random.rand(2000,10)
# make some NaNs in it:
for _ in range(1000):
i = np.random.randint(0,2000)
j = np.random.randint(0,10)
A[i,j] = np.nan
# get a matrix to plot with only the NaNs:
B = np.isnan(A)
# if NaN plot a point.
for i in range(2000):
for j in range(10):
if B[i,j]: plt.scatter(i,j)
plt.show()
when using python 2.6 or 2.7 consider using xrange instead of range for speedup.
Note. it could be faster to do:
C = np.where(B)
plt.scatter(C[0],C[1])

Categories

Resources