I am trying to create a bar range plot with a temporal x-axis with matplotlib. As an example see the following :
As far as I see, Matplotlib doesn't directly support this kind of plot.
What is the best way to achieve this?
Maybe its possible to adjust a boxplot or a fill_between plot?
just pass the bottom parameter to bar, e.g:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(10)
y_bot = np.linspace(30, 50, 10)
y_dif = np.linspace(10, 5, 10)
plt.bar(x, y_dif, bottom=y_bot)
Related
I'm facing a problem with making a 3D plot. I want to build a 3D surface plot like below from three columns of data.
Expected graphic case
I have implemented a few currently, as shown below.
Current picture case
But I still don't know how to make it "grid" like the first picture? Does anyone know how to achieve this? Part of the code and full data are as follows.
import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
import os
import warnings
from mpl_toolkits.mplot3d import Axes3D
warnings.filterwarnings('ignore')
os.chdir(r"E:\SoftwareFile\stataFile")
matplotlib.use('TkAgg')
plt.figure(figsize=(10,6))
data = pd.read_stata(r"E:\SoftwareFile\stataFile\demo.dta")
ax = plt.axes(projection="3d")
ax.plot_trisurf(data["age"], data["weight"], data["pr_highbp"],
cmap=plt.cm.Spectral_r)
ax.set_xticks(np.arange(20, 90, step=10))
ax.set_yticks(np.arange(40, 200, step=40))
ax.set_zticks(np.arange( 0, 1.2, step=0.2))
ax.set_title("Probability of Hypertension by Age and Weight")
ax.set_xlabel("Age (years)")
ax.set_ylabel("Weight (kg")
ax.zaxis.set_rotate_label(False)
ax.set_zlabel("Probability of Hypertension", rotation=90)
ax.view_init(elev=30, azim=240)
plt.savefig("demo.png", dpi=1200)
Download all data
Sincerely appreciate your help
Remove the colormap and opacity in the trisurf command like so:
ax.plot_trisurf(
data["age"],
data["weight"],
data["pr_highbp"],
color=None,
linewidth=1,
antialiased=True,
edgecolor="Black",
alpha=0,
)
That should result in:
You could also take a look at plot_wireframe(). For that I think you have to start with
x = data["age"].to_list()
y = data["weight"].to_list()
X, Y = np.meshgrid(x, y)
But I'm not sure how to create the z coordinate. It seems you may need interpolation from what I read.
Lets say I've 2 arrays
x = [1,2,3,4,5,6,7]
y = [1,2,2,2,3,4,5]
its scatter plot looks like this
what I want to do is that I want my x axis to look like this in the plot
0,4,8
as a result of which values of y in each piece of x should come closer .
The similar behavior I've seen is bar plots where this is called clustering , how do I do the same in case of scatter plot , or is there any other plot I should be using ?
I hope my question is clear/understandable .
All the help is appreciated
With you plot, try this, before you display the plot.
plt.xticks([0,4,8]))
or
import numpy as np
plt.xticks(np.arange(0, 8+1, step=4))
Then to change the scale you can try something like this,
plt.xticks([0,4,8]))
plt.rcParams["figure.figsize"] = (10,5)
I got this with my example,
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 30)
y = np.sin(x)
plt.xticks([0,4,8])
plt.rcParams["figure.figsize"] = (7,3)
plt.plot(x, y, 'o', color='black')
output
I think what you are looking for is close to swarmplots and stripplots in Seaborn. However, Seaborn's swarmplot and stripplot are purely categorical on one of the axes, which means that they wouldn't preserve the relative x-axis order of your elements inside each category.
One way to do what you want would be to increase the space in your x-axis between categories ([0,4,8]) and modify your xticks accordingly.
Below is an example of this where I assign the data to 3 different categories: [-2,2[, [2,6[, [6,10[. And each bar is dil_k away from its directly neighboring bars.
import matplotlib.pyplot as plt
import numpy as np
#Generating data
x= np.random.choice(8,size=(100))
y= np.random.choice(8,size=(100))
dil_k=20
#Creating the spacing between categories
x[np.logical_and(x<6, x>=2)]+=dil_k
x[np.logical_and(x<10, x>=6)]+=2*dil_k
#Plotting
ax=plt.scatter(x,y)
#Modifying axes accordingly
plt.xticks([0,2,22,24,26,46,48,50],[0,2,2,4,6,6,8,10])
plt.show()
And the output gives:
Alternatively, if you don't care about keeping the order of your elements along the x-axis inside each category, then you can use swarmplot directly.
The code can be seen below:
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
#Generating data
x= np.random.choice(8,size=(100))
y= np.random.choice(8,size=(100))
#Creating the spacing between categories
x[np.logical_and(x<2,x>=-2)]=0
x[np.logical_and(x<6, x>=2)]=4
x[np.logical_and(x<10, x>=6)]=8
#Plotting
sns.swarmplot(x=x,y=y)
plt.show()
And the output gives:
I am using the following code to create a polar plot of the sinus.
import numpy as np
import matplotlib.pyplot as plt
theta = np.arange(0, 2*np.pi, .01)[1:]
plt.polar(theta, sin(theta))
plt.show()
which produces:
but I want to plot it symmetrically, like this:
How can I get the result I want?
The matplotlib polar allows for negative radius. So, if you want the symmetric plot you need to plot the absolute value of sin:
polar(theta, abs(sin(theta)))
Anon, you need to plot the opposite of sin(theta):
plt.polar(theta, sin(theta))
plt.polar(theta, -sin(theta))
I would like to make beautiful scatter plots with histograms above and right of the scatter plot, as it is possible in seaborn with jointplot:
I am looking for suggestions on how to achieve this. In fact I am having some troubles in installing pandas, and also I do not need the entire seaborn module
I encountered the same problem today. Additionally I wanted a CDF for the marginals.
Code:
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
x = np.random.beta(2,5,size=int(1e4))
y = np.random.randn(int(1e4))
fig = plt.figure(figsize=(8,8))
gs = gridspec.GridSpec(3, 3)
ax_main = plt.subplot(gs[1:3, :2])
ax_xDist = plt.subplot(gs[0, :2],sharex=ax_main)
ax_yDist = plt.subplot(gs[1:3, 2],sharey=ax_main)
ax_main.scatter(x,y,marker='.')
ax_main.set(xlabel="x data", ylabel="y data")
ax_xDist.hist(x,bins=100,align='mid')
ax_xDist.set(ylabel='count')
ax_xCumDist = ax_xDist.twinx()
ax_xCumDist.hist(x,bins=100,cumulative=True,histtype='step',density=True,color='r',align='mid')
ax_xCumDist.tick_params('y', colors='r')
ax_xCumDist.set_ylabel('cumulative',color='r')
ax_yDist.hist(y,bins=100,orientation='horizontal',align='mid')
ax_yDist.set(xlabel='count')
ax_yCumDist = ax_yDist.twiny()
ax_yCumDist.hist(y,bins=100,cumulative=True,histtype='step',density=True,color='r',align='mid',orientation='horizontal')
ax_yCumDist.tick_params('x', colors='r')
ax_yCumDist.set_xlabel('cumulative',color='r')
plt.show()
Hope it helps the next person searching for scatter-plot with marginal distribution.
Here's an example of how to do it, using gridspec.GridSpec:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
x = np.random.rand(50)
y = np.random.rand(50)
fig = plt.figure()
gs = GridSpec(4,4)
ax_joint = fig.add_subplot(gs[1:4,0:3])
ax_marg_x = fig.add_subplot(gs[0,0:3])
ax_marg_y = fig.add_subplot(gs[1:4,3])
ax_joint.scatter(x,y)
ax_marg_x.hist(x)
ax_marg_y.hist(y,orientation="horizontal")
# Turn off tick labels on marginals
plt.setp(ax_marg_x.get_xticklabels(), visible=False)
plt.setp(ax_marg_y.get_yticklabels(), visible=False)
# Set labels on joint
ax_joint.set_xlabel('Joint x label')
ax_joint.set_ylabel('Joint y label')
# Set labels on marginals
ax_marg_y.set_xlabel('Marginal x label')
ax_marg_x.set_ylabel('Marginal y label')
plt.show()
I strongly recommend to flip the right histogram by adding these 3 lines of code to the current best answer before plt.show() :
ax_yDist.invert_xaxis()
ax_yDist.yaxis.tick_right()
ax_yCumDist.invert_xaxis()
The advantage is that any person who is visualizing it can compare easily the two histograms just by moving and rotating clockwise the right histogram on their mind.
On contrast, in the plot of the question and in all other answers, if you want to compare the two histograms, your first reaction is to rotate the right histogram counterclockwise, which leads to wrong conclusions because the y axis gets inverted. Indeed, the right CDF of the current best answer looks decreasing at first sight:
Is there an easy way to draw a zigzag or wavy line in matplotlib?
I'm aware of the different line styles (http://matplotlib.org/examples/lines_bars_and_markers/line_styles_reference.html), and I'm of course aware that instead of plotting
plt.figure(); plt.plot(n.linspace(0.7,1.42,100),[0.7]*100)
I could plot
plt.figure(); plt.plot(n.linspace(0.7,1.42,100),[0.69,0.71]*50)
for a zigzag-line, but I was wondering whether there was a more straightforward way?
Yes there is, but it comes with a little bit of fallout. The easiest way is to use the xkcd mode in matplotlib.
import numpy as np
import matplotlib.pyplot as plt
plt.xkcd()
plt.figure()
plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
plt.show()
Which gives you the following:
If you take a look at the code used to achieve this you will find that the xkcd function makes some changes to the rcParams dictionary. Most notably the entry rcParams['path.sketch'] = (scale, length, randomness) which is a path effect that is able to simulate a hand drawn look. The default parameters used by xkcd style are:
# explanation from the docstring of the xkcd function
scale = 1 # amplitude of the wiggle
length = 100 # length of the wiggle along the line
randomness = 2 # scale factor for shrinking and expanding the length
You can change the entries in the rcParams dictionary if you import it from the matplotlib package. In the following example I increased the randomness value from 2 to 100:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (1, 100, 100)
plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
plt.show()
Which will result in the following plot:
As you can see, more jiggling and the font used for the ticks is still 'normal'. However, the style is also used to draw the axes and so far I have not found a way around that.
Two workarounds could be:
Work without drawn borders/ spines.
Plot spines and line independently (hard and annoying to automize).
Dig through the documentation of matplotlib and path styles and find out if there is a way to set path styles only for a subset of drawn lines.
Option 1 can be achieved like this:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (10, 10, 100)
fig = plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
for pos, spine in fig[0].axes.spines.items():
spine.set_visible(False)
plt.show()
Which, in my opinion look quite ok. borders around plots are highly overrated anyways.
Edit: Less Chaos
To get an evenly waved line, set the randomness parameter to 1 and pick small values for amplitude and length:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams
rcParams['path.sketch'] = (3, 10, 1)
fig = plt.plot(np.linspace(0.7,1.42,100),[0.7]*100)
for pos, spine in fig[0].axes.spines.items():
spine.set_visible(False)
plt.show()
Bonus image: More Chaos
rcParams['path.sketch'] = (100, 1, 100)
You can apply the change in rcParams['path.sketch'] dictionary only to selected curves using with.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# prepare some fancy data
x = np.linspace(0,5,200)
y_0 = 10*x**0.2-x**1.5
y_1 = 20*np.sin(x)
y_2 = x**2
# prepare figure and axis
fig, ax = plt.subplots(nrows=1, ncols = 1, figsize = (5,3), dpi = 128)
# plot with some normal style
ax.plot(x, y_0, color = 'gray', ls='-.', lw = 2, label = 'normal style')
# now plot the wavy-like style!!!!
with mpl.rc_context({'path.sketch': (5, 15, 1)}):
ax.plot(x, y_1, color = 'blue', label = 'wavy style!')
# again plot with some different normal style
ax.plot(x, y_2, color = 'orange', ls = '-', lw = 3, label = 'again normal style')
ax.legend(loc='best') # turn on legend with automatic best location
plt.show()