I know this is well documented, but I'm struggling to implement this in my code.
I would like to shade the area under my graph with a colormap. Is it possible to have a colour, i.e. red from any points over 30, and a gradient up until that point?
I am using the method fill_between, but I'm happy to change this if there is a better way to do it.
def plot(sd_values):
plt.figure()
sd_values=np.array(sd_values)
x=np.arange(len(sd_values))
plt.plot(x,sd_values, linewidth=1)
plt.fill_between(x,sd_values, cmap=plt.cm.jet)
plt.show()
This is the result at the moment. I have tried axvspan, but this doesnt have cmap as an option. Why does the below graph not show a colormap?
I'm not sure if the cmap argument should be part of the fill_between plotting command. In your case probably want to use the fill() command btw.
These fill commands create polygons or polygon collections. A polygon collection can take a cmap but with fill there is no way of providing the data on which it should be colored.
What's (for as far as i know) certainly not possible is to fill a single polygon with a gradient as you wish.
The next best thing is to fake it. You can plot a shaded image and clip it based on the created polygon.
# create some sample data
x = np.linspace(0, 1)
y = np.sin(4 * np.pi * x) * np.exp(-5 * x) * 120
fig, ax = plt.subplots()
# plot only the outline of the polygon, and capture the result
poly, = ax.fill(x, y, facecolor='none')
# get the extent of the axes
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
# create a dummy image
img_data = np.arange(ymin,ymax,(ymax-ymin)/100.)
img_data = img_data.reshape(img_data.size,1)
# plot and clip the image
im = ax.imshow(img_data, aspect='auto', origin='lower', cmap=plt.cm.Reds_r, extent=[xmin,xmax,ymin,ymax], vmin=y.min(), vmax=30.)
im.set_clip_path(poly)
The image is given an extent which basically stretches it over the entire axes. Then the clip_path makes it only showup where the fill polygon is drawn.
I think all you need is to do the plot of the data one at a time, like:
import numpy
import matplotlib.pyplot as plt
import matplotlib.cm as cm
import matplotlib.colors as colors
# Create fake data
x = numpy.linspace(0,4)
y = numpy.exp(x)
# Now plot one by one
bar_width = x[1] - x[0] # assuming x is linealy spaced
for pointx, pointy in zip(x,y):
current_color = cm.jet( min(pointy/30, 30)) # maximum of 30
plt.bar(pointx, pointy, bar_width, color = current_color)
plt.show()
Resulting in:
Related
I have this script to extract data from an image and roi. I have everything working perfectly except the end when I output the graphs. Basically I'm having trouble with the windowing of both histograms. It doesn't matter if I change the gridsize, mincount, figure size, or x and y limits one of the histograms will always be slightly stretched. When I plot them individually they aren't stretched. Is there a way to make the hexagons on the same plot a consistent "non-stretched" shape?
Down below is my graph and plotting methods. (I left out my data extraction methods because it was quite specialized).
plt.ion()
plt.figure(figsize=(16,8))
plt.title('2D Histogram of Entorhinal Cortex ROIs')
plt.xlabel(x_inputs)
plt.ylabel(y_inputs)
colors = ['Reds','Blues']
x = []
y= []
#image extraction code
hist1 = plt.hexbin(x[0],y[0], gridsize=100,cmap='Reds',mincnt=10, alpha=0.35)
hist2 = plt.hexbin(x[1],y[1], gridsize=100,cmap='Blues',mincnt=10, alpha=0.35)
plt.colorbar(hist1, orientation="vertical")
plt.colorbar(hist2, orientation="vertical")
plt.ioff()
plt.show()
enter image description here
This issue can be solved by setting limits for the bins with the extent parameter. This can be done automatically by computing the minimum and maximum x and y values across all the data being plotted. In cases where gridsize is small (e.g. 10), this approach may result in some of the bins being partially outside of the plot limits. If so, setting a margin with plt.margins can help display all the bins within the plot.
import numpy as np # v 1.20.2
import matplotlib.pyplot as plt # v 3.3.4
# Create a random dataset
rng = np.random.default_rng(seed=123) # random number generator
size = 10000
x1 = rng.normal(loc=5, scale=10, size=size)
y1 = rng.normal(loc=5, scale=2, size=size)
x2 = rng.normal(loc=-30, scale=5, size=size)
y2 = rng.normal(loc=-20, scale=5, size=size)
# Define hexbin grid extent
xmin = min(*x1, *x2)
xmax = max(*x1, *x2)
ymin = min(*y1, *y2)
ymax = max(*y1, *y2)
ext = (xmin, xmax, ymin, ymax)
# Draw figure with colorbars
plt.figure(figsize=(10, 6))
hist1 = plt.hexbin(x1, y1, gridsize=30, cmap='Reds', mincnt=10, alpha=0.3, extent=ext)
hist2 = plt.hexbin(x2, y2, gridsize=30, cmap='Blues', mincnt=10, alpha=0.3, extent=ext)
plt.colorbar(hist1, orientation='vertical')
plt.colorbar(hist2, orientation='vertical')
# plt.margins(0.1) # Uncomment this if hex bins are partially outside of plot limits
plt.show()
The question is to read 10,000 coordinate points from a file and create a colored grid based on the density of each block on the grid. The range of x-axis is [-73.59, -73.55] and the y-axis is [45.49,45.530]. My code will plot a grid with many different colors, now I need a feature to only color the grid that has a specific density n, for example, The n = 100, only the grid with 100 points or higher will be colored to yellow, and other grids will be black.
I just added a link to my shapefile
https://drive.google.com/open?id=1H-8FhfonnPrYW9y7RQZDtiNLxVEiC6R8
import numpy as np
import matplotlib.pyplot as plt
import shapefile
grid_size = 0.002
x1 = np.arange(-73.59,-73.55,grid_size)
y1 = np.arange(45.49,45.530,grid_size)
shape = shapefile.Reader("Shape/crime_dt.shp",encoding='ISO-8859-1')
shapeRecords = shape.shapeRecords()
x_coordinates=[]
y_coordinates=[]
# read all points in .shp file, and store them in 2 lists.
for k in range(len(shapeRecords)):
x = float(shapeRecords[k].shape.__geo_interface__["coordinates"][0])
y = float(shapeRecords[k].shape.__geo_interface__["coordinates"][1])
x_coordinates.append(x)
y_coordinates.append(y)
plt.hist2d(x_coordinates,y_coordinates,bins=[x1,y1])
plt.show()
You can create a colormap with just two colors, and set vmin and vmax to be symmetrical around your desired pivot value.
Optionally you put the value of each bin inside the cells, while the pivot value decides the text color.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
grid_size = 0.002
x1 = np.arange(-73.59, -73.55, grid_size)
y1 = np.arange(45.49, 45.530, grid_size)
# read coordinates from file and put them into two lists, similar to this
x_coordinates = np.random.uniform(x1.min(), x1.max(), size=40000)
y_coordinates = np.random.uniform(y1.min(), y1.max(), size=40000)
pivot_value = 100
# create a colormap with two colors, vmin and vmax are chosen so that their center is the pivot value
cmap = ListedColormap(['indigo', 'gold'])
# create a 2d histogram with xs and ys as bin boundaries
binvalues, _, _, _ = plt.hist2d(x_coordinates, y_coordinates, bins=[x1, y1], cmap=cmap, vmin=0, vmax=2*pivot_value)
binvalues = binvalues.astype(np.int)
for i in range(len(x1) - 1):
for j in range(len(y1) - 1):
plt.text((x1[i] + x1[i + 1]) / 2, (y1[j] + y1[j + 1]) / 2, binvalues[i, j],
color='white' if binvalues[i, j] < pivot_value else 'black',
ha='center', va='center', size=8)
plt.show()
PS: If the bin values are very important, you can add them all as ticks. Then, their positions can also be used to draw gridlines as a division between the cells.
plt.yticks(y1)
plt.xticks(x1, rotation=90)
plt.grid(True, ls='-', lw=1, color='black')
To obtain contours based on these data, you could plt.contourf with the generated matrix. (You might want to use np.histogram2d to directly create the matrix.)
plt.contourf((x1[1:]+x1[:-1])/2, (y1[1:]+y1[:-1])/2, binvalues.T, levels=[0,100,1000], cmap=cmap)
I am creating a histogram for my data. Interestingly, when I plot my raw data and their histogram together on one plot, they are a "y-flipped" version of each other as follows:
I failed to find out the reason and fix it. My code snippet is as follows:
import math as mt
import numpy as np
import matplotlib.pylab as plt
x = np.random.randn(50)
y = np.random.randn(50)
w = np.random.randn(50)
leftBound, rightBound, topBound, bottomBound = min(x), max(x), max(y), min(y)
# parameters for histogram
x_edges = np.linspace(int(mt.floor(leftBound)), int(mt.ceil(rightBound)), int(mt.ceil(rightBound))-int(mt.floor(leftBound))+1)
y_edges = np.linspace(int(mt.floor(bottomBound)), int(mt.ceil(topBound)), int(mt.ceil(topBound))-int(mt.floor(bottomBound))+1)
# construct the histogram
wcounts = np.histogram2d(x, y, bins=(x_edges, y_edges), normed=False, weights=w)[0]
# wcounts is a 2D array, with each element representing the weighted count in a bins
# show histogram
extent = x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
histogram = axes.imshow(np.transpose(wcounts), extent=extent, alpha=1, vmin=0.5, vmax=5, cmap=cm.binary) # alpha controls the transparency
fig.colorbar(histogram)
# show data
axes.plot(x, y, color = '#99ffff')
Since the data here are generated randomly for demonstration, I don't think it helps much, if the problem is with that particular data set. But anyway, if it is something wrong with the code, it still helps.
By default, axes.imshow(z) places array element z[0,0] in the top left corner of the axes (or the extent in this case). You probably want to either add the origin="bottom" argument to your imshow() call or pass a flipped data array, i.e., z[:,::-1].
I am creating a histogram for my data. Interestingly, when I plot my raw data and their histogram together on one plot, they are a "y-flipped" version of each other as follows:
I failed to find out the reason and fix it. My code snippet is as follows:
import math as mt
import numpy as np
import matplotlib.pylab as plt
x = np.random.randn(50)
y = np.random.randn(50)
w = np.random.randn(50)
leftBound, rightBound, topBound, bottomBound = min(x), max(x), max(y), min(y)
# parameters for histogram
x_edges = np.linspace(int(mt.floor(leftBound)), int(mt.ceil(rightBound)), int(mt.ceil(rightBound))-int(mt.floor(leftBound))+1)
y_edges = np.linspace(int(mt.floor(bottomBound)), int(mt.ceil(topBound)), int(mt.ceil(topBound))-int(mt.floor(bottomBound))+1)
# construct the histogram
wcounts = np.histogram2d(x, y, bins=(x_edges, y_edges), normed=False, weights=w)[0]
# wcounts is a 2D array, with each element representing the weighted count in a bins
# show histogram
extent = x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]
fig = plt.figure()
axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)
axes.set_xlabel('x (m)')
axes.set_ylabel('y (m)')
histogram = axes.imshow(np.transpose(wcounts), extent=extent, alpha=1, vmin=0.5, vmax=5, cmap=cm.binary) # alpha controls the transparency
fig.colorbar(histogram)
# show data
axes.plot(x, y, color = '#99ffff')
Since the data here are generated randomly for demonstration, I don't think it helps much, if the problem is with that particular data set. But anyway, if it is something wrong with the code, it still helps.
By default, axes.imshow(z) places array element z[0,0] in the top left corner of the axes (or the extent in this case). You probably want to either add the origin="bottom" argument to your imshow() call or pass a flipped data array, i.e., z[:,::-1].
I have one question about the grid lines matplotlib.
I am not sure if this is possible to do or not.
I am plotting the following graph as shown in the image.
I won't give the entire code, since it is involving reading of files.
However the important part of code is here -
X, Y = np.meshgrid(smallX, smallY)
Z = np.zeros((len(X),len(X[0])))
plt.contourf(X, Y, Z, levels, cmap=cm.gray_r, zorder = 1)
plt.colorbar()
...
# Set Border width zero
[i.set_linewidth(0) for i in ax.spines.itervalues()]
gridLineWidth=0.1
ax.set_axisbelow(False)
gridlines = ax.get_xgridlines()+ax.get_ygridlines()
#ax.set_axisbelow(True)
plt.setp(gridlines, 'zorder', 5)
ax.yaxis.grid(True, linewidth=gridLineWidth, linestyle='-', color='0.6')
ax.xaxis.grid(False)
ax.xaxis.set_ticks_position('none')
ax.yaxis.set_ticks_position('none')
Now, my questions is like this -
If I put the grid lines below the contour, they disappear since they are below it.
If I put the grid line above the contour, they looks like what they are looking now.
However, what I would like to have is the grid lines should be visible, but should be below the black portion of the contour. I am not sure if that is possible.
Thank You !
In addition to specifying the z-order of the contours and the gridlines, you could also try masking the zero values of your contoured data.
Here's a small example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(-2*np.pi, 2*np.pi, 0.1)
y = np.arange(-2*np.pi, 2*np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) - np.cos(Y)
Z = np.ma.masked_less(Z, 0) # you use mask_equal(yourData, yourMagicValue)
fig, ax = plt.subplots()
ax.contourf(Z, zorder=5, cmap=plt.cm.coolwarm)
ax.xaxis.grid(True, zorder=0)
ax.yaxis.grid(True, zorder=0)
And the output: