Given a function f(x), how to ?
Generate the graph of the function and its derivative function on a figure with one bottom and top subplots that share the x-axis.
Both axes take their form with an input array with values from min to max
that are spaced by 0.5.
Each subplot must have a title
The top subplot must have the ylabel, whereas the bottom subplot must have both xlabel and ylabel
Each subplot shot have different styles of lines (colors, thickness, ...) and x ticks must also be in a different style(specifically the fontsize and rotation).
I did this :
#the function is f(x) while the derivative is df(x)
x = np.arange(min, max, 0.5) #scale
figure, (top, bottom) = plt.subplots(2, sharex=True, figsize = [5.0, 7.0])
top.plot(x, f(x), 'b', linewidth = 5)
top.set_title(Function f(x))
top.ylabel('f(x)')
figure.set_xticks(colors = 'r', fontsize = 12, rotation = 30)
bottom.plot(x, df(x), 'g-', linewidth = 8)
bottom.set_title('Derivative function of f(x)')
bottom.xlabel('x')
bottom.ylabel('df(x)')
plt.show(figure)
But it does not work. How could I fix everything ?
Edited for generalization
This seems to do what you need:
import numpy as np
import matplotlib.pyplot as plt
def f(x):
return np.sin(x) - x*np.cos(x) #The derivative is equal to x
x = np.arange(-5.0, 5.0, 0.05)
figure, (top, bottom) = plt.subplots(2, sharex=True, figsize = [5.0, 7.0])
top.plot(x, f(x), 'b', linewidth = 5)
top.set_title('Function f(x) = sin(x) - x*cos(x)')
top.set_ylabel('f(x)')
plt.tick_params(axis='x', colors = 'r', labelsize = 12, rotation = 30)
bottom.plot(x, x, 'g-', linewidth = 8)
bottom.set_title('Derivative function of f(x)')
bottom.set_xlabel('x')
bottom.set_ylabel('df(x)')
bottom.tick_params(axis='x', colors = 'r')
plt.show()
When something goes wrong, it really helps to read Python's error messages. If you don't understand the message, you can google it, which often leads to a useful StackOverflow answer.
For example, the message "AttributeError: 'AxesSubplot' object has no attribute 'ylabel'" leads to this post. In this case, the correct function name is .set_ylabel(). The difference between the "Object Oriented Interface" and the "old" "plt" interface, can be a bit confusing (it is due to historic reasons). It helps to give the "axes" returned by plt.subplots() a name such as ax_top to more easily figure out how example code maps to your situation.
To set the color, rotation etc. of the ticks, you can use ax.tick_params(axis='x', rotation=30, labelsize=12, labelcolor='red'). This can be a bit tricky, the only way to "know" is to search for it on StackOverflow.
There are many different line styles. They can be set via the linestyle=... keyword. The most common ones also can be part of the code in the third parameter to plot(), e.g. ax.plot(x, y, 'b--') would use a blue dashed line style. (See also the introductory tutorial.)
Also note that plt.show() doesn't get the figure as parameter.
import matplotlib.pyplot as plt
import numpy as np
def f(x):
return np.sin(x) - x * np.cos(x) # The derivative is equal to x
x = np.arange(-5.0, 5.0, 0.05)
figure, (ax_top, ax_bottom) = plt.subplots(2, sharex=True, figsize=[5.0, 7.0])
ax_top.plot(x, f(x), 'b', linewidth=1)
ax_top.set_title('Function f(x) = sin(x) - x*cos(x)')
ax_top.set_ylabel('f(x)')
ax_bottom.tick_params(axis='x', rotation=30, labelsize=12, labelcolor='red')
ax_bottom.plot(x, x, 'g--', linewidth=2)
ax_bottom.set_title('Derivative function of f(x)')
ax_bottom.set_xlabel('x')
ax_bottom.set_ylabel('df(x)')
plt.tight_layout() # fits the axes and text nicely into the figure
plt.show()
Related
I am trying to make use the polar plot projection to make a radar chart. I would like to know how to put only one grid line in bold (while the others should remain standard).
For my specific case, I would like to highlight the gridline associated to the ytick "0".
from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
#Variables
sespi = pd.read_csv("country_progress.csv")
labels = sespi.country
progress = sespi.progress
angles=np.linspace(0, 2*np.pi, len(labels), endpoint=False)
#Concatenation to close the plots
progress=np.concatenate((progress,[progress[0]]))
angles=np.concatenate((angles,[angles[0]]))
#Polar plot
fig=plt.figure()
ax = fig.add_subplot(111, polar=True)
ax.plot(angles, progress, '.--', linewidth=1, c="g")
#ax.fill(angles, progress, alpha=0.25)
ax.set_thetagrids(angles * 180/np.pi, labels)
ax.set_yticklabels([-200,-150,-100,-50,0,50,100,150,200])
#ax.set_title()
ax.grid(True)
plt.show()
The gridlines of a plot are Line2D objects. Therefore you can't make it bold. What you can do (as shown, in part, in the other answer) is to increase the linewidth and change the colour but rather than plot a new line you can do this to the specified gridline.
You first need to find the index of the y tick labels which you want to change:
y_tick_labels = [-100,-10,0,10]
ind = y_tick_labels.index(0) # find index of value 0
You can then get a list of the gridlines using gridlines = ax.yaxis.get_gridlines(). Then use the index you found previously on this list to change the properties of the correct gridline.
Using the example from the gallery as a basis, a full example is shown below:
r = np.arange(0, 2, 0.01)
theta = 2 * np.pi * r
ax = plt.subplot(111, projection='polar')
ax.set_rmax(2)
ax.set_rticks([0.5, 1, 1.5, 2]) # less radial ticks
ax.set_rlabel_position(-22.5) # get radial labels away from plotted line
ax.grid(True)
y_tick_labels = [-100, -10, 0, 10]
ax.set_yticklabels(y_tick_labels)
ind = y_tick_labels.index(0) # find index of value 0
gridlines = ax.yaxis.get_gridlines()
gridlines[ind].set_color("k")
gridlines[ind].set_linewidth(2.5)
plt.show()
Which gives:
It is just a trick, but I guess you could just plot a circle and change its linewidth and color to whatever could be bold for you.
For example:
import matplotlib.pyplot as plt
import numpy as np
Yline = 0
Npoints = 300
angles = np.linspace(0,360,Npoints)*np.pi/180
line = 0*angles + Yline
ax = plt.subplot(111, projection='polar')
plt.plot(angles, line, color = 'k', linewidth = 3)
plt.ylim([-1,1])
plt.grid(True)
plt.show()
In this piece of code, I plot a line using plt.plot between any point of the two vectors angles and line. The former is actually all the angles between 0 and 2*np.pi. The latter is constant, and equal to the 'height' you want to plot that line Yline.
I suggest you try to decrease and increase Npoints while having a look to the documentaion of np.linspace() in order to understand your problem with the roundness of the circle.
I have an issue making a simple plot while setting the x-axis in python.
Here is my code:
import import matplotlib.pyplot as plt
y = [2586.087776040828,2285.8044466570227,1991.0556336526986,1719.7261325405243,1479.8272625661773,1272.5176077500348,1096.4367842436593,949.02201512882527,826.89866676342137,726.37921828890637,636.07392349697909,553.52559247838076,480.71257022562935,418.00424110010181,364.41801903538288,318.67575156686001,280.17668207838426,248.15399589447813,221.75070551820284,199.59983992701842,179.72014852370447,162.27141772637697,147.14507926321306,134.22828323366301,123.36572367962557,114.33589702168332,106.8825327470323,100.69181027167537,95.515144406404971,91.091036326792434]
x = range(0,30)
fig3_4 ,ax3_4 = plt.subplots()
ax3_4.semilogx(range(0,30),(loss_ave_hist))
ax3_4.set_title('Validation Performance')
# ax3_4.set_xticks(np.arange(0,30, 1.0))
ax3_4.set_xlabel('i')
ax3_4.set_ylabel('Average Loss')
fig3_4.show()
plt.show()
I believe my code is right! Notice the line of code I commented, it should set
the axis with the values I want, however, it throws an error. I cannot figure
out why!
Here is my plot from the my plot:
Here is a way to make a semilogx plot but with xticks labelled according to their original (non-log) values.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
y = np.array([2586.087776040828, 2285.8044466570227, 1991.0556336526986, 1719.7261325405243, 1479.8272625661773, 1272.5176077500348, 1096.4367842436593, 949.02201512882527, 826.89866676342137, 726.37921828890637, 636.07392349697909, 553.52559247838076, 480.71257022562935, 418.00424110010181, 364.41801903538288, 318.67575156686001, 280.17668207838426, 248.15399589447813, 221.75070551820284, 199.59983992701842, 179.72014852370447, 162.27141772637697, 147.14507926321306, 134.22828323366301, 123.36572367962557, 114.33589702168332, 106.8825327470323, 100.69181027167537, 95.515144406404971, 91.091036326792434])
x = np.arange(1, len(y)+1)
fig, ax = plt.subplots()
ax.plot(x, y, 'o-')
ax.set_xlim(x.min(), x.max())
ax.set_xscale('log')
formatter = mticker.ScalarFormatter()
ax.xaxis.set_major_formatter(formatter)
ax.xaxis.set_major_locator(mticker.FixedLocator(np.arange(0, x.max()+1, 5)))
plt.show()
yields
FixedLocator(np.arange(0, x.max()+1, 5))) places a tick mark at every 5th value in x.
With ax.xaxis.set_major_locator(mticker.FixedLocator(x)), the xticklabels got a bit too crowded.
Note I changed x = range(0, 30) to x = np.arange(1, len(y)+1) since
the length of x should match the length of y and since we are using a logarithmic x-axis, it does not make sense to start at x=0.
Notice also that in your original code the first y value (2586.08...) is missing since its associated x value, 0, is off-the-chart on a logarithmic scale.
I used the following and it ran without errors.
All I changed is the typo in your first line of your imports, and replaced loss_ave_hist with y (i.e. what you called your data in your question.
y = [2586.087776040828,2285.8044466570227,1991.0556336526986,1719.7261325405243,1479.8272625661773,1272.5176077500348,1096.4367842436593,949.02201512882527,826.89866676342137,726.37921828890637,636.07392349697909,553.52559247838076,480.71257022562935,418.00424110010181,364.41801903538288,318.67575156686001,280.17668207838426,248.15399589447813,221.75070551820284,199.59983992701842,179.72014852370447,162.27141772637697,147.14507926321306,134.22828323366301,123.36572367962557,114.33589702168332,106.8825327470323,100.69181027167537,95.515144406404971,91.091036326792434]
import matplotlib.pyplot as plt
fig3_4 ,ax3_4 = plt.subplots()
x = range(0,30)
ax3_4.semilogx(range(0,30),(y))
ax3_4.set_title('Validation Performance')
# ax3_4.set_xticks(np.arange(0,30, 1.0))
ax3_4.set_xlabel('i')
ax3_4.set_ylabel('Average Loss')
plt.show()
UPDATE: I understand you want to label the x-axis with values from 0..29, but on a log scale, all those numbers are very close.
Here is an image with xticks set (I din't get any errors):
fig3_4 ,ax3_4 = plt.subplots()
x = range(0,30)
ax3_4.semilogx(range(0,30),(y))
ax3_4.set_title('Validation Performance')
ax3_4.set_xticks(np.arange(0,30, 1.0))
ax3_4.set_xlabel('i')
ax3_4.set_ylabel('Average Loss')
plt.show()
Here is an image where I replace semilogx with semilogy.
fig3_4 ,ax3_4 = plt.subplots()
x = range(0,30)
ax3_4.semilogy(range(0,30),(y))
ax3_4.set_title('Validation Performance')
ax3_4.set_xticks(np.arange(0,30, 1.0))
ax3_4.set_xlabel('i')
ax3_4.set_ylabel('Average Loss')
plt.show()
Does any of this resemble your goal?
I'm currently trying to place a horizontal dashed line through my log-log graph using the below code. K2H_HUBp[:,1] and DivR are two [1x6000] arrays. The variable ones is a [1x6000] array full of ones.
The point of this plot is showing how the radii of "potatoes" compares with "sweet potatoes". Hence if they were the same, all of the data points should fall on this y = 1 line.
plt.scatter(K2H_HUBp[:,1],DivR,s=2.5,alpha=0.15,c = '#A9A9A9')
plt.loglog(K2H_HUBp[:,1], ones, '--',dashes=(1, 1),linewidth=0.9,c='#3C323C')
plt.ylim((0.1,10))
plt.xlim((0.35,12))
ax = plt.gca()
ax.tick_params(which = 'both', direction = 'in',right='on',top='on')
ax.set_xscale('log')
ax.set_yscale('log')
plt.ylabel("Radius (Potatos/Sweet Potatos)")
plt.xlabel("Radius (Potatos)")
I'd like the ones line to be equally dashed through the plot. I have the problem of getting this graph here where the lines aren't equally spaced out.
I'm looking for the graph to be very similar to this one (yes this is a linear graph and I'm working with a log-log graph)
I've tried modifying the dashes() parameters with no luck.
Thanks in advance for your guidance. :)
You can either plot it with an other loglog-plot or with a standard plot. Is this code giving you what you're after?
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1)
x = np.linspace(0.01, 10, 100)
y = x**5
ax1.loglog(x, y, '.')
ax1.plot([x[0], x[-1]], [y[0], y[-1]], '--', label='with plot')
ax1.legend()
ax2.loglog(x, y, '.')
ax2.loglog([x[0], x[-1]], [y[0], y[-1]], '--', label='with loglog')
ax2.legend()
fig.show()
# plt.show()
So it turns out Pyplot has a nifty function called hlines. This function just draws a horizontal line using the following arguments:
matplotlib.pyplot.hlines(y, xmin, xmax, colors='k', linestyles='solid', label='', hold=None, data=None, **kwargs)
In my case i've now completely removed the code:
plt.loglog(K2H_HUBp[:,1], ones, '--',dashes=(1, 1),linewidth=0.9,c='#3C323C')
and have replaced it with:
plt.hlines(1, 0.001, 20, linestyles='dashed',linewidth=0.9,colors='#3C323C')
plotting a y = 1 line from x 0.001 to x 20. This then gives me my desired result being this graph.
Thanks for all your guidance and I hope this helps someone else in the future!
Dear python/matplotlib community,
I am having an issue within matplotlib: I can't seem to plot multiple overlaid histograms in the same plot space using the following:
binsize = 0.05
min_x_data_sey, max_x_data_sey = np.min(logOII_OIII_sey), np.max(logOII_OIII_sey)
num_x_bins_sey = np.floor((max_x_data_sey - min_x_data_sey) / binsize)
min_x_data_comp, max_x_data_comp = np.min(logOII_OIII_comp), np.max(logOII_OIII_comp)
num_x_bins_comp = np.floor((max_x_data_comp - min_x_data_comp) / binsize)
min_x_data_sf, max_x_data_sf = np.min(logOII_OIII_sf), np.max(logOII_OIII_sf)
num_x_bins_sf = np.floor((max_x_data_sf - min_x_data_sf) / binsize)
axScatter_farright = fig.add_subplot(gs_right[0,0])
axScatter_farright.tick_params(axis='both', which='major', labelsize=10)
axScatter_farright.tick_params(axis='both', which='minor', labelsize=10)
axScatter_farright.set_ylabel(r'$\mathrm{N}$', fontsize='medium')
axScatter_farright.set_xlim(-1.5, 1.0)
axScatter_farright.set_xlabel(r'$\mathrm{log([OII]/[OIII])}$', fontsize='medium')
axScatter_farright.hist(logOII_OIII_sey, num_x_bins_sey, ec='0.3', fc='none', histtype='step')
axScatter_farright.hist(logOII_OIII_comp, num_x_bins_comp, ec='0.3', fc='none', histtype='step')
axScatter_farright.hist(logOII_OIII_sf, num_x_bins_sf, ec='0.3', fc='none', histtype='step')
It seems like the axes class can not handle multiple histograms? Please correct me if and/or where I have gone wrong.
My overall plot is a 1 row, 3 column plotting space. I would like to use grid spec to give the plots a good layout.
This is what my plot looks like thus far:
This is what I want the histogram portion of the figure to look like in terms of the step type histogram overlays (with legend):
I have the datasets as three different tuple type arrays generated from a csv file. i.e., using x, y = np.genfromtext(datafile.csv)
If anyone is able to explain how this could be done I would be very appreciative.
What you're doing should work perfectly. Is it possible that only one of the distributions is in the x-range of -1.5 to 1 that you've set a couple of lines before? (i.e. Try removing the manual set_xlim statement and see if the other distributions show up.)
As a quick, stand-alone example to demonstrate that things should work:
import numpy as np
import matplotlib.pyplot as plt
num = 1000
d1 = np.random.normal(-1, 1, num)
d2 = np.random.normal(1, 1, num)
d3 = np.random.normal(0, 3, num)
fig, ax = plt.subplots()
ax.hist(d1, 50, ec='red', fc='none', lw=1.5, histtype='step', label='Dist A')
ax.hist(d2, 50, ec='green', fc='none', lw=1.5, histtype='step', label='Dist B')
ax.hist(d3, 100, ec='blue', fc='none', lw=1.5, histtype='step', label='Dist C')
ax.legend(loc='upper left')
plt.show()
(If you want the legend to show lines instead of boxes, you'll need use a proxy artist. I can add an example if you'd like. That's outside the scope of this question, though.)
Is there a function in matplotlib similar to MATLAB's line extensions?
I am basically looking for a way to extend a line segment to a plot. My current plot looks like this.
After looking at another question and applying the formula, I was able to get it to here, but it still looks messy.
Does anyone have the magic formula here?
Have a go to write your own as I don't think this exists in matplotlib. This is a start, you could improve by adding the semiinfinite etc
import matplotlib.pylab as plt
import numpy as np
def extended(ax, x, y, **args):
xlim = ax.get_xlim()
ylim = ax.get_ylim()
x_ext = np.linspace(xlim[0], xlim[1], 100)
p = np.polyfit(x, y , deg=1)
y_ext = np.poly1d(p)(x_ext)
ax.plot(x_ext, y_ext, **args)
ax.set_xlim(xlim)
ax.set_ylim(ylim)
return ax
ax = plt.subplot(111)
ax.scatter(np.linspace(0, 1, 100), np.random.random(100))
x_short = np.linspace(0.2, 0.7)
y_short = 0.2* x_short
ax = extended(ax, x_short, y_short, color="r", lw=2, label="extended")
ax.plot(x_short, y_short, color="g", lw=4, label="short")
ax.legend()
plt.show()
I just realised you have some red dots on your plots, are those important? Anyway the main point I think you solution so far is missing is to set the plot limits to those that existed before otherwise, as you have found, they get extended.
New in matplotlib 3.3
There is now an axline method to easily extend arbitrary lines:
Adds an infinitely long straight line. The line can be defined either by two points xy1 and xy2
plt.axline(xy1=(0, 1), xy2=(1, 0.5), color='r')
or defined by one point xy1 and a slope.
plt.axline(xy1=(0, 1), slope=-0.5, color='r')
Sample data for reference:
import numpy as np
import matplotlib.pyplot as plt
x, y = np.random.default_rng(123).random((2, 100)) * 2 - 1
m, b = -0.5, 1
plt.scatter(x, y, c=np.where(y > m*x + b, 'r', 'k'))