I'm trying to make a 1D heatmap for a gene (see ref 1 in pastebin for example). I've gotten close to what I'm looking for with contourf, but I haven't been able to figure out how to get exactly what I'm looking for. Basically, I'm want to utilize a colormap that has 10 discrete colors, and the cutoffs for the different colors correspond to the percentiles of the data (so the top 10% of the data points are red, the next 10% are orange, etc).
I don't have enough reputation to post more than two links or any images, so you can also see my output images from the code below, as well as the other pages I've looked at to try and solve this question, at http://pastebin.com/jAkxyQsK.
The actual data points are in a list at http://pastebin.com/3TrkkpZ0. You can try with random integers, but the difference between linear scaling and percentile-scaling likely won't be clear unless your data is skewed like mine.
data = [] #actually a list of ~450 floats
x = []
nd = np.array(data)
x = np.empty([2, nd.shape[0]])
x[:,:] = nd
fig = plt.figure(figsize = (11, 8.5))
ax = fig.add_subplot(111)
Now, here are my experiments:
mind, maxd, sprd = min(data), max(data), max(data)-min(data)
levels = [(lambda n: mind + (n*sprd)/10)(n) for n in range(0,11,1)]
hm = plt.contourf(x, levels = levels, cmap = "rainbow")
cbar = fig.colorbar(hm, ax = ax)
plt.show()
[Figure 1 on pastebin]
This is mostly what I want to see: the colorbar is discretized and the plot looks fine, but the colorbar is spaced linearly between the max and the min of the data, which is not what I want. Attempt two:
levels = np.percentile(data, [z for z in range (0,110,10)])
hm = plt.contourf(x, levels = levels, cmap = "rainbow")
cbar = fig.colorbar(hm, ax = ax)
plt.show()
[Figure 2 on pastebin]
This is also close; the colorbar is divided up by the values of the percentiles (or at least the tick values indicate that), but for some reason it's no longer utilizing the full range of the colormap and I have no idea why.
I also tried implementing the function described in references 2 and 3 with pcolor, but I couldn't figure out how to make them work with my data instead of a scatter plot and the results were not as close as I could get with contourf, so I stopped pursuing them. If the answer is already in one of the links I've looked at but I couldn't understand it, then a 'plain English' translation would be super helpful.
I cannot tell why the colormap does not use the full range of colors in your example, but it seems that the following is closer to the result you want (i.e. it does span a larger range of colors with the quantile levels).
...
hm = plt.contourf(x, levels = levels, cmap = "rainbow", vmax = levels[-2])
...
You can also try a 'weighted' value for the max colormap level.
...
hm = plt.contourf(x, levels = levels, cmap = "rainbow", vmax = 0.3 * levels[-1] + 0.7 * levels[-2])
...
Related
I have data with lots of x values around zero and only a few as you go up to around 950,
I want to create a plot with a non-linear x axis so that the relationship can be seen in a 'straight line' form. Like seen in this example,
I have tried using plt.xscale('log') but it does not achieve what I want.
I have not been able to use the log scale function with a scatter plot as it then only shows 3 values rather than the thousands that exist.
I have tried to work around it using
plt.plot(retper, aep_NW[y], marker='o', linewidth=0)
to replicate the scatter function which plots but does not show what I want.
plt.figure(1)
plt.scatter(rp,aep,label="SSI sum")
plt.show()
Image 3:
plt.figure(3)
plt.scatter(rp, aep)
plt.xscale('log')
plt.show()
Image 4:
plt.figure(4)
plt.plot(rp, aep, marker='o', linewidth=0)
plt.xscale('log')
plt.show()
ADDITION:
Hi thank you for the response.
I think you are right that my x axis is truncated but I'm not sure why or how...
I'm not really sure what to post code wise as the data is all large and coming from a server so can't really give you the data to see it with.
Basically aep_NW is a one dimensional array with 951 elements, values from 0-~140, with most values being small and only a few larger values. The data represents a storm severity index for 951 years.
Then I want the x axis to be the return period for these values, so basically I made a rp array, of the same size, which is given values from 951 down decreasing my a half each time.
I then sort the aep_NW values from lowest to highest with the highest value being associated with the largest return value (951), then the second highest aep_NW value associated with the second largest return period value (475.5) ect.
So then when I plot it I need the x axis scale to be similar to the example you showed above or the first image I attatched originally.
rp = [0]*numseas.shape[0]
i = numseas.shape[0] - 1
rp[i] = numseas.shape[0]
i = i - 1
while i != 0:
rp[i] = rp[i+1]/2
i = i - 1
y = np.argsort(aep_NW)
fig, ax = plt.subplots()
ax.scatter(rp,aep_NW[y],label="SSI sum")
ax.set_xscale('log')
ax.set_xlabel("Return period")
ax.set_ylabel("SSI score")
plt.title("AEP for NW Europe: total loss per entire extended winter season")
plt.show()
It looks like in your "Image 3" the x axis is truncated, so that you don't see the data you are interested in. It appears this is due to there being 0's in your 'rp' array. I updated the examples to show the error you are seeing, one way to exclude the zeros, and one way to clip them and show them on a different scale.
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
n = 100
numseas = np.logspace(-5, 3, n)
aep_NW = np.linspace(0, 140, n)
rp = [0]*numseas.shape[0]
i = numseas.shape[0] - 1
rp[i] = numseas.shape[0]
i = i - 1
while i != 0:
rp[i] = rp[i+1] /2
i = i - 1
y = np.argsort(aep_NW)
fig, axes = plt.subplots(1, 3, figsize=(14, 5))
ax = axes[0]
ax.scatter(rp, aep_NW[y], label="SSI sum")
ax.set_xscale('log')
ax.set_xlabel("Return period")
ax.set_ylabel("SSI score")
ax = axes[1]
rp = np.array(rp)[y]
mask = rp > 0
ax.scatter(rp[mask], aep_NW[y][mask], label="SSI sum")
ax.set_xscale('log')
ax.set_xlabel("Return period (0 values excluded)")
ax = axes[2]
log2_clipped_rp = np.log2(rp.clip(2**-100, None))[y]
ax.scatter(log2_clipped_rp, aep_NW[y], label="SSI sum")
xticks = list(range(-110, 11, 20))
xticklabels = [f'$2^{{{i}}}$' for i in xticks]
ax.set_xticks(xticks)
ax.set_xticklabels(xticklabels)
ax.set_xlabel("log$_2$ Return period (values clipped to 2$^{-100}$)")
plt.show()
I am trying to plot the energy consumption profile of an electric vehicle. I am using the elevation profile vs the horizontal distance the vehicle runs along a path. I want to add a second x-axis on top of the plot to represent by each chunk of distance, what the energy consumption value was at that precise location.
This is what I have so far, but it's not precisely what I need:
I know this should be fairly simple as it is only adding a second x-axis that matches with the primary x-axis, but I have wasted an entire day trying to figure out unsuccessfully :(
Any insights will be greatly appreciated.
Code:
fig, ax1 = plt.subplots()
elevation_distance_np = elevation_distance.to_numpy()
plt.plot(elevation_distance_np[:,0], elevation_distance_np[:,1], color = 'blue')
plt.grid(True)
plt.xlabel("Distancia recorrida")
plt.ylabel("Elevación de distancia recorrrida")
axes2 = ax1.twiny()
axes2.set_xticks(suma_kWh_np[::mth.ceil(len(suma_kWh_np)/8)])
plt.title("Elevación vs Distancia Recorrida")
plt.show()
This is a not so trivial endeavor, as these questions show, so don't feel frustrated for not getting this on your own.
Disclaimer: this is not the most elegant solution, but it works. I made a toy example where the conversion from one axis to the other is obtained by dividing the main by 8.5. Also, I replotted your data on this secondary axis, to set the values of its own X axis to something sensible, then removed this extra line.
x = np.linspace(0, 140) # Some x values, similar to your range
# Caps them to a minimum of 0
y = np.clip(x * (-1) + 100, a_min=0, a_max=100)
# Creates something similar to your data
elevation_distance_np = np.hstack((x[:, np.newaxis], y[:, np.newaxis]))
# I guessed some transform. If you don't have a formula,
# you'll need to interpolate between known values, probably.
suma_kWh_np = x / 8.5
fig, ax1 = plt.subplots()
# Changed to explicit notation, so we don't go back and forth between them
ax1.plot(elevation_distance_np[:,0], elevation_distance_np[:,1], color = 'blue')
ax1.grid(True)
ax1.set_xlabel("Distancia recorrida")
ax1.set_ylabel("Elevación de distancia recorrrida")
ax2 = ax1.twiny()
# Added a copy of your line, but which will be removed later
extra_line = ax2.plot(suma_kWh_np, elevation_distance_np[:,1], color = 'r')
# Now, we get the x ticks and transform them to kWh.
# Here, I had to remove the first and last points ([1:-1])
# because ax1.get_xticks() returned a range from -20 to 160,
ax2.set_xticks(ax1.get_xticks()[1:-1] / 8.5)
ax1.set_title("Elevación vs Distancia Recorrida")
ax2.lines.pop() # We remove the temporary line right before plotting
plt.show()
Here's the result.
I'm trying to plot the contour map of a given function f(x,y), but since the functions output scales really fast, I'm losing a lot of information for lower values of x and y. I found on the forums to work that out using vmax=vmax, it actually worked, but only when plotted for a specific limit of x and y and levels of the colormap.
Say I have this plot:
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
u = np.linspace(-2,2,1000)
x,y = np.meshgrid(u,u)
z = (1-x)**2+100*(y-x**2)**2
cont = plt.contour(x,y,z,500,colors='black',linewidths=.3)
cont = plt.contourf(x,y,z,500,cmap="jet",vmax=100)
plt.colorbar(cont)
plt.show
I want to uncover whats beyond the axis limits keeping the same scale, but if I change de x and y limits to -3 and 3 I get:
See how I lost most of my levels since my max value for the function at these limits are much higher. A work around to this problem is to increase the levels to 1000, but that takes a lot of computational time.
Is there a way to plot only the contour levels that I need? That is, between 0 and 100.
An example of a desired output would be:
With the white space being the continuation of the plot without resizing the levels.
The code I'm using is the one given after the first image.
There are a few possible ideas here. The one I very much prefer is a logarithmic representation of the data. An example would be
from matplotlib import ticker
fig = plt.figure(1)
cont1 = plt.contourf(x,y,z,cmap="jet",locator=ticker.LogLocator(numticks=10))
plt.colorbar(cont1)
plt.show()
fig = plt.figure(2)
cont2 = plt.contourf(x,y,np.log10(z),100,cmap="jet")
plt.colorbar(cont2)
plt.show()
The first example uses matplotlibs LogLocator functions. The second one just directly computes the logarithm of the data and plots that normally.
The third example just caps all data above 100.
fig = plt.figure(3)
zcapped = z.copy()
zcapped[zcapped>100]=100
cont3 = plt.contourf(x,y,zcapped,100,cmap="jet")
cbar = plt.colorbar(cont3)
plt.show()
I want to color the line in a plot based on the following of a data set on the y axis.
if data > 0:
color = 'r'
if data = 0:
color = 'g'
if data < 0:
color = 'b'
Unfortunately I only know how to color the entire data set one color. I also couldn't find anything on the web. I'm assuming there is a way to do this without breaking up the dataset for every time the color changes.
Below is an example of plotting the data with just one color.
import matplotlib.pyplot as plt
import numpy as np
# Simple data
x = np.linspace(0, 2 * np.pi, 400)
data = np.sin(x ** 2)
#plot
f, ax = plt.subplots()
ax.plot(x, data, color='r')
plt.show()
The color parameter actually can take a list as an argument. For example, here's a simple bit of code that sets up a list of colors based on whether the data is positive or negative:
colors = []
for item in data:
if item < 0:
colors.append('r')
else:
colors.append('g')
then simply:
ax.bar(x, data, color=colors)
Edit: So I tested it, and it appears that my answer is only applicable for bar graphs. I couldn't find anything in the matplotlib documentation that seemed to indicate that coloring a line plot with multiple colors was possible. I did, however find this site, which I believe has the information you want. The guy there defines his own function to achieve it.
Using the file at my link, here is an equivalent version for a line graph:
cmap = ListedColormap(['r', 'g']) # use the colors red and green
norm = BoundaryNorm([-1000,0,1000], cmap.N) # map red to negative and green to positive
# this may work with just 0 in the list
fig, axes = plt.subplots()
colorline(x, data, data, cmap=cmap, norm=norm)
plt.xlim(x.min(), x.max())
plt.ylim(data.min(), data.max())
plt.show()
The last three arguments of colorline here tell it the color data and how to map it.
How can I change the data on one axis?
I'm making some spectrum analysis on some data and my x-axis is the index of some matrix. I'd like to change it so that the x-axis becomes the data itself.
I'm using the imshow() to plot the data (I have a matrix whose elements are some intensity, the y axes are their detector-source correspondent pair and the x-axis should be their frequency).
The code for it is written down here:
def pltspec(dOD, self):
idx = 0
b = plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B = np.zeros((2*len(self.Chan),len(b[0])))
for idx in range(2*len(self.Chan)):
b = plt.psd(dOD[:,idx],Fs=self.fs,NFFT=512)
B[idx,:] = 20*log10(b[0])
fig = plt.figure()
ax = fig.add_subplot(111)
plt.imshow(B, origin = 'lower')
plt.colorbar()
locs, labels = xticks(find(b[1]), b[1])
plt.axis('tight')
ax.xaxis.set_major_locator(MaxNLocator(5))
I think if there's a way of interchanging the index of some array with its value, my problem would be solved.
I've managed to use the line locs, labels = xticks(find(b[1]), b[1]). But with it on my graph my axis interval just isn't right... I think it has something to do with the MaxNLocator (which I used to decrease the number of ticks).
And if I use the xlim, I can set the figure to be what I want, but the x axis is still the same (on that xlim I had to use the original data to set it right).
What am I doing wrong?
Yes, you can use the xticks method exemplified in this example.
There are also more sophisticated ways of doing it. See ticker.