I want to plot the RGB histograms of an image using numpy.histogram.
(See my function draw_histogram below)
It works well for a regular range of [0, 255] :
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread('Bulbasaur.jpeg')
draw_histogram(im, minimum=0., maximum=255.)
What I want to do :
I expect the images I use to have out of range values. Sometimes they will be out of range, sometimes not. I want to use the RGB histogram to analyse how bad the values are out of range.
Let's say I expect the values to be at worst in the interval [-512, 512]. I still want the histogram to display the in-range intensities at the right spot, and leave blank the unpopulated range sections. For example, if I draw the histogram of Bulbasaur.jpeg again but with range [-512, 512], I expect to see the same histogram but contracted along the "x" axis (between the two dashed lines in the histogram below).
The problem :
When I try to draw the histogram for an unregular range, something goes wrong :
import numpy as np
import matplotlib.pyplot as plt
im = plt.imread('Bulbasaur.jpeg')
draw_histogram(im, minimum=-512., maximum=512.)
My code for draw_histogram() :
def draw_histogram(im, minimum, maximum):
fig = plt.figure()
color = ('r','g','b')
for i, col in enumerate(color):
hist, bins = np.histogram(im[:, :, i], int(maximum-minimum), (minimum, maximum))
plt.plot(hist, color=col)
plt.xlim([int(minimum), int(maximum)])
# Draw vertical lines to easily locate the 'regular range'
plt.axvline(x=0, color='k', linestyle='dashed')
plt.axvline(x=255, color='k', linestyle='dashed')
plt.savefig('Histogram_Bulbasaur.png')
plt.close(fig)
return 0
Question
Does anyone know a way of properly drawing RGB histogram with unregular ranges?
You should pass x values to 'plt.plot'
I changed:
plt.plot(hist, color=col)
to this:
plt.plot(np.arange(minimum,maximum),hist, color=col)
With this change, the graph began to appear normally. Essentially, plt.plot was trying to start plotting the y-values you gave it from np.hist starting at 0. This works when your expected range starts at 0, but when you want to include negative numbers, plt.plot shouldn't start at 0, rather, it should start at minimum, so using np.range to manually assign x values fixes the problem.
Related
How do you plot a scatter plot for an array result_array of shape (1087, 2) that looks like this:
array([[-1.89707840e+03, 3.99819932e+00],
[-2.55018840e+03, -2.61913223e+00],
[-1.85480840e+03, -2.36545732e-01],
...,
[-1.64432840e+03, 9.79555441e+00],
[-1.59022840e+03, 1.08955493e+01],
[-1.73963840e+03, 3.60132161e-01]])
?
Update:
Tried:
import matplotlib.pyplot as plt
plt.scatter(result_array[:, 0], result_array[:, 1])
plt.show()
and the plot looks like this:
Assuming that the array is X:
import matplotlib.pyplot as plt
plt.scatter(X[:, 0], X[:, 1])
plt.show()
plt.scatter() has many addional options, see the documentation for details.
Answer to the updated question:
It seems that you have an outlier row in the array with the first coordinate close to 2.5*10^6 (which gives the point close to the right margin of the plot), while other rows have their first coordinates smaller by a few orders of magnitude. For example, the rows in the part of the array visible in the question have first coordinates close to -2000. For this reason, these rows are squished into what looks like a vertical line in the plot.
There are two possible ways to fix it:
If you really have only one (or just a few) outliers, you can remove them from the array and possibly plot them separately.
Alternatively, if you want to plot all points at once, then using the logarithmic scale on the x-axis may help. Since you have some points with negative first coordinates, you would need to use the symmetric logarithmic scale - which is logarithmic in both positive and negative directions of the x-axis.:
import matplotlib.pyplot as plt
plt.scatter(X[:, 0], X[:, 1])
plt.xscale('symlog')
plt.show()
I am trying to plot rectangles inside a circle and color each rectangle based on their value. Each rectangle depicts the failure rate at that position. Initially I have divided the range of values into 5 intervals of 20 limits each and assigned a fixed color as below example.
{'0-20':'Yellow', '21-40':'Orange', '41-60':'Coral', '61-80':'Red', '81-100':'Black'}
Later, I scrapped the idea and went with the 'plasma' cmap from matplotlib.colors.cmaps. It ideally colored the rectangle in the shades of yellow to purple. However, it misses the small data point values to show on the plot. I am looking for the flexibility of changing the range of intervals.
from matplotlib import cm
plasma = cm.get_cmap('plasma', 30)
Ideally I want something as below. If the max value of a rectangle is 92 and min value is 0. I want to divide the range into 6 intervals and plot them based on the interval . Attached is the color bar I am looking for.
Is there a way to achieve this in matplotlib? Kindly help.
Edit:
Adding few more details.
I am not looking for the fixed color , rather I am looking for gradient which intensifies from lower limit to upper limit of the range in each interval.For example in the attached picture all value between 0 to 15.33 have the color intensifying from yellow to red.
I agree with ImportanceOfBeingErnest's comment that single colors in a legend or a regular colorbar might be helpful. Here is an example of how the former could be created using colorbar tick labelling:
# import modules
import numpy as np
import matplotlib.pyplot as plt
# select colormap
cmap = plt.cm.get_cmap('plasma')
# define bins
bins = [0, 15.333, 30.666, 46, 61.333, 76.666, 92]
# create dummy array for heatmap
imag = np.reshape(np.linspace(0, 92, 50), (10, -1))
# prepare tick positions
pairs = list(zip(bins[:-1], bins[1:]))
labs = ['']*len(bins) + ['%05.2f ≤ x < %05.2f' % pair for pair in pairs]
bins = bins + [0.5*sum(pair) for pair in pairs]
plt.imshow(imag, cmap=cmap, aspect='auto', origin='lower')
# plot colorbar
cbar = plt.colorbar(ticks=bins)
cbar.ax.set_yticklabels(labs)
plt.tight_layout()
I have plotted a histogram. But I want to plot discrete lines instead of three bars. Is there any way to do that?
import matplotlib.pyplot as plt
w1 = [-2,-2,-2,-2,0,0,0,1,1,1,1,1,1]
n,bins,patches = plt.hist(w1,bins=10)
plt.xlabel("bins")
plt.ylabel("counts")
plt.show()
If you just want to plot bars with smaller width
Use the argument rwidth, for relative width of each histogram bar compared to the bin size. Experiment different values for different visual results. Example:
w1=[-2,-2,-2,-2,0,0,0,1,1,1,1,1,1]
n,bins,patches=plt.hist(w1,bins=10, rwidth=0.1)
plt.xlabel("bins")
plt.ylabel("counts")
plt.show()
If you actually want to plot lines instead of bars
Loop over each value inside w1 and call plt.plot on a line from XY (value, 0) to XY (value, number of times value appears in w1). Example:
for value in w1:
plt.plot([value, value], [0, w1.count(value)], color='b')
plt.show()
Note that I've used the argument color='b' so that matplotlib wouldn't make different colors for each line. Also, by default matplotlib adds some whitespace to surrounding lines when we call plt.plot, so you may want to call plt.ylim(bottom=0), so that the bars do not appear to "float" above the plot.
Inside of the plt.hist(...) ad a variable: rwidth (relative width) with a value bellow 1, that way you will get bars with lower width.
Read more about that here: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist.html#matplotlib.pyplot.hist
I would propose to use a stem plot with the counts of the unique elements of the data. Of course this only makes sense for discrete data.
import numpy as np
import matplotlib.pyplot as plt
w1 = [-2,-2,-2,-2,0,0,0,1,1,1,1,1,1]
u, c = np.unique(w1, return_counts=True)
plt.stem(u,c, use_line_collection=True, basefmt="none")
plt.ylim(0,None)
plt.xlabel("bins")
plt.ylabel("counts")
plt.show()
I have considered the matrix of numbers as the mapping of plane points to their values and then draw the isoline. I have practised the contour function from the following module: matplitlib.pylab and I have succeeded in configurating it as I wanted, but there is one exeption. - The contourf set a step by default and it designates the values on the Y-axis and X-axis. I would like to change it. Is it possible? I have read all of the documentation about the contour function but I haven't found the solution. I am a beginner. This is my code:
plt.figure()
cs = plt.contour(m.Z, levels=A, colors=K, linestyles='solid')
plt.clabel(cs, A[1::2], inline=0.5, fmt='%1.0f', fontsize=10)
plt.colorbar(cs, shrink=1)
plt.show()
m.Z is my matrix and plt is matplotlib.pylab I have tried to change the step giving the values of vectors - X and Y in contour ( I know that they aren't needed) but it doesn't work.
I am trying to generate a heatmap of a 10x10 matrix. All values in the matrix are probabilities; sum of all elements equal to 1.0. I decided to use the matshow plot type (it seemed easy to use), however I cannot generate the output I'd like to have so far.
1.Visually it looks kinda ugly. Would you recommend a fitting color map for use in a heatmap?
2.Is there a way to assign predefined bins to the color map when using matshow? E.g. take a gradient of 1000 colors, always use the same colors for the corresponding probabilities. In default behavior, I think matshow checks the minimum and maximum values, assigned the first and last colors in the gradient to those values, then colorizes the values in between by interpolation.
Sometimes I have very similar probabilities in the matrix, and other times the range of probabilities may be great. Due to the default behavior I tried to explain above, I get similar plots, which makes comparisons harder.
My code for generating the said heat maps (and an example plot) is below by the way.
Thanks!
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
def pickcoord():
i = np.random.randint(0,10)
j = np.random.randint(0,10)
return [i,j]
board = np.zeros((10,10))
for i in range(1000000):
try:
direction = np.random.randint(0,2)
new_board = np.zeros((10,10))
coords = pickcoord()
if direction == 1:
for k in range(2):
new_board[coords[0]][coords[1]+k] = 1
else:
for k in range(2):
new_board[coords[0]+k][coords[1]] = 1
except IndexError:
new_board = np.zeros((10,10))
board = board + new_board
board_prob = board/np.sum(board)
plt.figure(figsize=(6,6))
plt.matshow(board_prob, cmap=matplotlib.cm.Spectral_r, interpolation='none')
plt.xticks(np.arange(0.5,10.5), [])
plt.yticks(np.arange(0.5,10.5), [])
plt.grid()
Your second problem can be solved using the vmin and vmax arguments of the matshow function:
matshow(board_prob, cmap=cm.Spectral_r, interpolation='none', vmin=0, vmax=1)
Considering your first problem, it depends on what you want to emphasize or display. Choose a fitting colormap from the default colormaps of matplotlib.