Fix location of stippling for subplots - python

I have been trying to stipple contour plots to show locations where values are statistically significant. However, when I do this in subplots where the significance is the same, the stippling looks different based on the random location of the filled stipples. I have reproduced the problem below. Is there a way to fix the location of the stipples so that they look the same when plotted? Or is there a better way to stipple plots?
These 2 subplots are plotting the exact same data, but the stipples look different.
import numpy as np
from matplotlib import pyplot as plt
#Create some random data
x = np.arange(0,100,1)
x,y = np.meshgrid(x,x)
stipp = 10*np.random.rand(len(x),len(x))
fig =plt.figure(figsize=(12,8))
ax1 = plt.subplot(121)
ax2 = plt.subplot(122)
#Plot stippling
ax1.contourf(x,y,stipp,[0,4],colors='none',hatches='.')
ax2.contourf(x,y,stipp,[0,4],colors='none',hatches='.')
plt.show()

So in case anyone wants to know, the best way to stipple multiple subplots with similar statistical significance is to use a scatter plot as recommended above instead of contouring. Just make sure to sample the data sparingly so you don't have a high density of dots next to each other.
import numpy as np
from matplotlib import pyplot as plt
#Create some random data
x = np.arange(0,100,1)
x,y = np.meshgrid(x,x)
stipp = 10*np.random.rand(len(x),len(x))
fig =plt.figure(figsize=(12,8))
ax1 = plt.subplot(121)
ax2 = plt.subplot(122)
#Plot stippling
ax1.scatter(x[(stipp<=4) & (stipp>=0)][::5],y[(stipp<=4) & (stipp>=0)][::5])
ax2.scatter(x[(stipp<=4) & (stipp>=0)][::5],y[(stipp<=4) & (stipp>=0)][::5])
plt.show()

Related

Matplotlib: How to recreate `6 petal` polar diagram

For an assignment, I have to recreate the following plot (including all labels and ticks):
This is what I have tried so far with my code
import numpy as np
import matplotlib.pyplot as plt
nmax=101 # choose a high number to "smooth out" lines in plots
x = np.linspace(0,20,nmax) # create an array x
y_br = np.sin(3*x) # y for the bottom right subplot
fig = plt.figure()
ax4 = plt.subplot(224, projection = 'polar')
ax4.plot(x, y_br, 'tab:blue')
But if you were to run this yourself, this does not replicate the plot. What function could be used here and how can tick marks be changed in polar plots? Thanks in advance?

Matplotlib: orthographic projection of 3D data (in 2D plot)

I'm trying to plot 3D data in 2D using orthographic projection. Here is partially what I'm looking for:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')
axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.scatter(values[:,axis1],values[:,axis2])
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()
There are some issues with this plot and the fixes I tried: I would need 'equal' aspect so that the circles are actually circle. I would also need the circles to be of the same size in each subplot. Finally, I would like the space to be optimized (i.e. with as little white space inside and between the subplots as possible).
I have tried sharing the axis between the subplots, then doing .axis('scaled') or .set_aspect('equal','box',share=True) for each axes, but the axis end up not being properly shared, and the circle in each subplot end up of different sizes. And while it crops the subplots to the data, it leaves a lot of space between the subplots. .axis('equal') or .set_aspect('equal','datalim',share=True) without axis shared leaves white space inside the subplots, and with shared axis, it leaves out some data.
Any way to make it work? And it would be perfect if it can work on matplotlib 3.4.3.
You can use a common xlim, ylim for your subplots and set your equal ratio with ax.set_aspect(aspect='equal', adjustable='datalim'):
See full code below:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
fig = plt.figure(figsize=(10,10),facecolor='white')
axs = [fig.add_subplot(223)]
axs.append(fig.add_subplot(224))#,sharey=axs[0]))
axs.append(fig.add_subplot(221))#,sharex=axs[0]))
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.scatter(values[:,axis1],values[:,axis2])
ax.set_xlim([np.amin(values),np.amax(values)])
ax.set_ylim([np.amin(values),np.amax(values)])
ax.set_aspect('equal', adjustable='datalim')
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
fig.subplots_adjust(.08,.06,.99,.99,0,0)
plt.show()
The output gives:
I made it work using gridspec (I changed scatter for plot to visually make sure no data gets left out). It requires some tweaking of the figsize to really minimize the white space within the axes. Thank you to #jylls for the intermediate solution.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
%matplotlib inline
rng = np.random.default_rng(12345)
values = rng.random((100,3))-.5
values[:,1] = 1.6*values[:,1]
values[:,2] = .5*values[:,2]
fig = plt.figure(figsize=(10,8),facecolor='white')
ranges = np.ptp(values,axis=0)
gs = GridSpec(2, 2, None,.08,.06,.99,.99,0,0, width_ratios=[ranges[0], ranges[1]], height_ratios=[ranges[1], ranges[2]])
axs = [fig.add_subplot(gs[2])]
axs.append(fig.add_subplot(gs[3]))#,sharey=axs[0]))
axs.append(fig.add_subplot(gs[0]))#,sharex=axs[0]))
for ax,axis in zip(axs,['y','x','z']):
axis1,axis2={'x':(1,2),'y':(0,2),'z':(0,1)}[axis]
ax.add_patch(plt.Circle([0,0], radius=.2, color='pink',zorder=-20))
ax.plot(values[:,axis1],values[:,axis2])
ax.set_aspect('equal', adjustable='datalim')
axs[0].set_xlabel('x')
axs[2].set_ylabel('y')
axs[1].set_xlabel('y')
axs[0].set_ylabel('z')
plt.show()

Multiple graphs instead one using Matplotlib

The code below takes a dataframe filters by a string in a column and then plot the values of another column
I plot the values of the using histogram and than worked fine until I added Mean, Median and standard deviation but now I am just getting an empty graph where instead the all of the variables mentioned below should be plotted in one graph together with their labels
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import pyplot as plt
from matplotlib import pyplot as plt
import numpy as np
df = pd.read_csv(r'C:/Users/output.csv', delimiter=";", encoding='unicode_escape')
df['Plot_column'] = df['Plot_column'].str.split(',').str[0]
df['Plot_column'] = df['Plot_column'].astype('int64', copy=False)
X=df[df['goal_colum']=='start running']['Plot_column'].values
dev_x= X
mean_=np.mean(dev_x)
median_=np.median(dev_x)
standard_=np.std(dev_x)
plt.hist(dev_x, bins=5)
plt.plot(mean_, label='Mean')
plt.plot(median_, label='Median')
plt.plot(standard_, label='Std Deviation')
plt.title('Data')
https://matplotlib.org/3.1.1/gallery/statistics/histogram_features.html
There are two major ways to plot in matplotlib, pyplot (the easy way) and ax (the hard way). Ax lets you customize your plot more and you should work to move towards that. Try something like the following
num_bins = 50
fig, ax = plt.subplots()
# the histogram of the data
n, bins, patches = ax.hist(dev_x, num_bins, density=1)
ax.plot(np.mean(dev_x))
ax.plot(np.median(dev_x))
ax.plot(np.std(dev_x))
# Tweak spacing to prevent clipping of ylabel
fig.tight_layout()
plt.show()

Manually draw log-spaced tick marks and labels in matplotlib

I frequently find myself working in log units for my plots, for example taking np.log10(x) of data before binning it or creating contour plots. The problem is, when I then want to make the plots presentable, the axes are in ugly log units, and the tick marks are evenly spaced.
If I let matplotlib do all the conversions, i.e. by setting ax.set_xaxis('log') then I get very nice looking axes, however I can't do that to my data since it is e.g. already binned in log units. I could manually change the tick labels, but that wouldn't make the tick spacing logarithmic. I suppose I could also go and manually specify the position of every minor tick such it had log spacing, but is that the only way to achieve this? That is a bit tedious so it would be nice if there is a better way.
For concreteness, here is a plot:
I want to have the tick labels as 10^x and 10^y (so '1' is '10', 2 is '100' etc.), and I want the minor ticks to be drawn as ax.set_xaxis('log') would draw them.
Edit: For further concreteness, suppose the plot is generated from an image, like this:
import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()
x_range = [-5,3] # log10 units
y_range = [-55, -45] # log10 units
p = plt.imshow(img,extent=x_range+y_range)
plt.show()
and all we want to do is change the axes appearance as I have described.
Edit 2: Ok, ImportanceOfBeingErnest's answer is very clever but it is a bit more specific to images than I wanted. I have another example, of binned data this time. Perhaps their technique still works on this, though it is not clear to me if that is the case.
import numpy as np
import pandas as pd
import datashader as ds
from matplotlib import pyplot as plt
import scipy.stats as sps
v1 = sps.lognorm(loc=0, scale=3, s=0.8)
v2 = sps.lognorm(loc=0, scale=1, s=0.8)
x = np.log10(v1.rvs(100000))
y = np.log10(v2.rvs(100000))
x_range=[np.min(x),np.max(x)]
y_range=[np.min(y),np.max(y)]
df = pd.DataFrame.from_dict({"x": x, "y": y})
#------ Aggregate the data ------
cvs = ds.Canvas(plot_width=30, plot_height=30, x_range=x_range, y_range=y_range)
agg = cvs.points(df, 'x', 'y')
# Create contour plot
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(agg, extent=x_range+y_range)
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()
The general answer to this question is probably given in this post:
Can I mimic a log scale of an axis in matplotlib without transforming the associated data?
However here an easy option might be to scale the content of the axes and then set the axes to a log scale.
A. image
You may plot your image on a logarithmic scale but make all pixels the same size in log units. Unfortunately imshow does not allow for such kind of image (any more), but one may use pcolormesh for that purpose.
import numpy as np
import matplotlib.pyplot as plt
import scipy.misc
img = scipy.misc.face()
extx = [-5,3] # log10 units
exty = [-45, -55] # log10 units
x = np.logspace(extx[0],extx[-1],img.shape[1]+1)
y = np.logspace(exty[0],exty[-1],img.shape[0]+1)
X,Y = np.meshgrid(x,y)
c = img.reshape((img.shape[0]*img.shape[1],img.shape[2]))/255.0
m = plt.pcolormesh(X,Y,X[:-1,:-1], color=c, linewidth=0)
m.set_array(None)
plt.gca().set_xscale("log")
plt.gca().set_yscale("log")
plt.show()
B. contour
The same concept can be used for a contour plot.
import numpy as np
from matplotlib import pyplot as plt
x = np.linspace(-1.1,1.9)
y = np.linspace(-1.4,1.55)
X,Y = np.meshgrid(x,y)
agg = np.exp(-(X**2+Y**2)*2)
fig, ax = plt.subplots()
plt.gca().set_xscale("log")
plt.gca().set_yscale("log")
exp = lambda x: 10.**(np.array(x))
cf = ax.contourf(exp(X), exp(Y),agg, extent=exp([x.min(),x.max(),y.min(),y.max()]))
ax.set_xlabel("x")
ax.set_ylabel("y")
plt.show()

Python matplotlib graph problem

import matplotlib
import matplotlib.pyplot as plt
import pylab as PL
matplotlib.rcParams['axes.unicode_minus'] = False
fig = plt.figure()
ax = fig.add_subplot(111)
PL.loglog(a, b,'o')
ax.set_title('Graph Example')
plt.show()
1) This displays the graph with points on the plot. Is there a way to join these points with a smooth curve.
2) I want to draw more than one plot in the same graph(i.e. for a different set of values of lists a and b) . How do I do that? I want to represent points of each graph with a different symbol(cross,square,circle) or color.
See #Ber's comment
Simply call PL.loglog multiple times.

Categories