I want to plot points on the interval x in [0, 4]. My function performs something interesting for very small values, so I would like to create a non-linear scale that would use more space for smaller values of x. Logarithmic scale would be a great solution, but the problem is that my x-axis must include 0, which is not part of logarithmic axis.
So I considered using a power scale. After some googling I came across the following solution.
def stratify(ax, power=2):
f = lambda x: (x + 1)**(1 / power)
f_inv = lambda y: y**power - 1
ax.set_xscale('function', functions=(f, f_inv))
x = np.linspace(0, 4, 100)
y = np.sqrt(x)
fig, ax = plt.subplots()
ax.plot(x, y)
stratify(ax, 2)
plt.show()
The function stratify changes the x-scale of the plot to the square root function. This looks kind of correct. Below is a minimal example plot corresponding to the above code (not actual data).
I would like to have control over the nonlinearity in the x-scale, that is why I have introduced the power parameter. However, when I change the power parameter to value different from 2, the plot does not change at all. This is very surprising for me. I would appreciate if somebody could advise me how I can control the extent of non-linearity in x-axis. If possible, I would like it even more non-linear, making the interval [0, 0.5] take half of the plot.
EDIT While the current solution by #Thomas works as intended, the plotting routine throws a lot of errors when one attempts to do anything with it. For example, I would like to insert extra ticks, as the original only has integer ticks for whatever reason. Inserting extra ticks via ax.set_xticks(ax.get_xticks() + [0.5]) results in an error posx and posy should be finite values. What is this error, and how can it be bypassed?
For me, there's a change when switching from power=2 to power=10. Just be careful to edit it at the right position, i.e. when calling stratify=X.
Here's power=2:
Here's power=10:
However, here's another suggestion that is slightly more aggressive:
import numpy as np
import matplotlib.pyplot as plt
def stratify(ax, scale=1):
f = lambda x: np.log(x / scale + 1)
f_inv = lambda y: scale * (np.exp(y) - 1)
ax.set_xscale('function', functions=(f, f_inv))
x = np.linspace(0, 4, 100)
y = np.sqrt(x)
fig, axs = plt.subplots(1, 3)
for i, scale in enumerate([10,1,0.1]):
ax = axs[i]
ax.set_title(f'Scale={scale}')
ax.plot(x, y)
stratify(ax, scale=scale)
plt.show()
Resulting in
Another option are zoom regions.
Related
I want to make a demonstration for the approximation of an integral of a continuous function with piecewise constant step functions.
The resulting plot should look something like this:
I have the piece constant function, my problem is that I don't know how to plot it since the typical candidates don't seem to work:
It looks similar to a histogram, but is generated very differently.
And from what I have seen bar-charts don't align to the number line.
The plt.step() method on the other hand does not include the bins/bars; using it I got this so far:
with this code
kwargs = dict(drawstyle = 'steps-mid')
plt.plot(times, f(times), '-')
plt.plot(times, fitted_values, **kwargs)
Is there a dedicated function, or some kwargs argument that I overlooked, that can plot what I need here?
Edit:
Thank you for the answer #Stef ! I just tried this solution and recognized an issue with a bar-plot here.
plt generates a bar for every value in the times array. Now I get this result:
You can use bar with the align parameter:
import numpy as np
x = np.linspace(0, 1, 11)
y = x**2 + 1
plt.plot(x, y, 'r-')
plt.bar(x, y, width=0.1, align='edge', fc='lightgreen', ec='k')
plt.xlim(0, 1)
I made a 2d histogram of two variables(x and y) and each of them are long, 1d arrays. I then calculated the average of x in each bin and want to make the colorbar show how much each x is above or below average in the respective bin.
So far I have tried to make a new array, z, that contains the values for how far above/below average each x is. When I try to use this with pcolormesh I run into issues that it is not a 2-D array. I also tried to solve this issue by following the solution from this problem (Using pcolormesh with 3 one dimensional arrays in python). The length of each array (x, y and z) are equal in this case and there is a respective z value for each x value.
My overall goal is to just have the colorbar not dependent on counts but to have it show how much above/below average each x value is from the average x of the bin. I suspect that it may make more sense to just plot x vs. z but I do not think that would fix my colorbar issue.
As LoneWanderer mentioned some sample code would be useful; however let me make an attempt at what you want.
import numpy as np
import matplotlib.pyplot as plt
N = 10000
x = np.random.uniform(0, 1, N)
y = np.random.uniform(0, 1, N) # Generating x and y data (you will already have this)
# Histogram data
xbins = np.linspace(0, 1, 100)
ybins = np.linspace(0, 1, 100)
hdata, xedges, yedged = np.histogram2d(x, y, bins=(xbins, ybins))
# compute the histogram average value and the difference
hdataMean = np.mean(hdata)
hdataRelDifference = (hdata - hdataMean) / hdataMean
# Plot the relative difference
fig, ax = plt.subplots(1, 1)
cax = ax.imshow(hdataRelDifference)
fig.colorbar(cax, ax=ax)
If this is not what you intended, hopefully there are enough pieces here to adapt it for your needs.
There are several related questions (here, here, and here), but the suggested solutions don't work in my case.
I'm creating subplots iteratively, so I don't know ahead of time the width of each one (it gets calculated AFTER plt.subplots() is called), which means I can't set the size of each subplot when I initially create them.
I would like to set the size of the subplot x axis after it has already been created.
Imagine something like:
items = [A,B,C] #this could have any number of items in it
f,ax = plt.subplots(len(items),1, figsize=(10,10)) #figsize is arbitrary and could be anything
for i in range(len(items)):
#calculate x and y data for current item
#calculate width of x axis for current item
plt.sca(ax[i])
cax = plt.gca()
cax.plot(x,y)
#here is where I would like to set the x axis size
#something like cax.set_xlim(), but for the size, not the limit
Note 1: The units don't matter, but the relative size does, so it could be size in pixels, or centimeters, or even a ratio calculated based on the relative widths.
Note 2: The width of the x axis is NOT related in this case to the x limit, so I can't just set the x limit and expect the axis to scale correctly.
Also, I'm trying to keep this code short, since it's to be shared with people unfamiliar with Python, so if the only solution involves adding a bunch of lines, it's not worth it and I'll live with incorrectly scaled axes. This is an aesthetic preference but not a requirement.
Thanks!
EDIT: Here's what I'm aiming for
You can create a new GridSpec specifying the height_ratios and then updating each axs position:
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
# create figure
f, ax = plt.subplots(3, 1, figsize=(10,10))
# plot some data
ax[0].plot([1, 2, 3])
ax[1].plot([1, 0, 1])
ax[2].plot([1, 2, 20])
# adjust subplot sizes
gs = GridSpec(3, 1, height_ratios=[5, 2, 1])
for i in range(3):
ax[i].set_position(gs[i].get_position(f))
plt.show()
I asked a similar question before here. The use case was slightly different, but it might still be helpful.
Surely now you got the answer or this problem is deprecated but if someone else is searching, I solved this problem using "Bbox". The idea is something like this:
from matplotlib.transforms import Bbox
fig, ax = plt.subplots(3,1, figsize = (11,15))
ax[0].set_position(Bbox([[0.125, 0.6579411764705883], [0.745, 0.88]]))
ax[2].set_position(Bbox([[0.125, 0.125], [0.745, 0.34705882352941175]]))
For more information, check https://matplotlib.org/api/transformations.html#matplotlib.transforms.Bbox
I am plotting some data that includes spatial (x, y) components as well as a z component, which is the value of the measurement at that point in space. I was looking at the gallery, and I'm just not getting it. I think that what I want is a pcolormesh, but I don't understand what I need to put in for arguments. I finally had success getting a scatter plot to do basically what I want, but it's less pretty than I want. If I could figure out a way to make the points in the scatter plot bigger, I would be a lot happier with my plot. Furthermore, I am stuck on trying to add a legend - I only need the colorbar portion, since the end user doesn't really care about the X and Y dimensions. Looking at the colorbar example, it seems that I need to add an axis, but I don't understand how I'm telling it that the axis I need is the Z axis.
x_vals = list(first_array[data_loc_dictionary['x_coord_index']][:])
y_vals = list(first_array[data_loc_dictionary['y_coord_index']][:])
y_vals = [-i for i in y_vals]
z_vals = list(first_array[data_loc_dictionary['value_index']][:])
plt.scatter(x_vals, y_vals, s = len(x_vals)^2, c = z_vals, cmap = 'rainbow')
plt.show()
Here is an example of what I am trying to duplicate:
And here is what the code above produces:
I would like the second to look a little more like the first, i.e., if there were a way to adjust the markers to be large enough to approximate that look, that would be ideal
I am struggling with creating a legend. Colorbar seems to be the way to go, but I am not comprehending how to specify that it needs to be based on the Z values.
Good catch with the ^2 -
What about this basic example:
# generate random data
In [63]: x = np.random.rand(20)
In [64]: y = np.random.rand(20)
In [65]: z = np.random.rand(20)
# plot it with square markers: marker='s'
In [66]: plt.scatter(x, y, s=len(x)**2, c=z, cmap='rainbow', marker='s')
Out[66]: <matplotlib.collections.PathCollection at 0x39e6c90>
# colorbar
In [67]: c = plt.colorbar(orientation='horizontal')
In [68]: c.set_label('This is a colorbar')
In [69]: plt.show()
The Size of the points is given by
s : scalar or array_like, shape (n, ), optional, default: 20
size in points^2.
I see no reason why s=len(x)**2 is a good choice by default. I would play around with it according to your preference.
In case you want to know how to replicate your initial example image with pcolormesh, I would do:
import numpy as np
import matplotlib.pyplot as plt
f, ax = plt.subplots(figsize=(6, 5))
grid = np.arange(-5, 6)
x, y = np.meshgrid(grid, grid)
z = np.random.randn(len(x), len(y))
mask = (np.abs(x) + np.abs(y)) > 4
z = np.ma.masked_array(z, mask)
mesh = ax.pcolormesh(x - .5, y - .5, z, cmap="coolwarm", vmin=-3, vmax=3)
plt.colorbar(mesh)
To produce:
I'm trying to make a point and figure chart. I can get it to work printing out on the terminal but I want to graph it with matplotlib. What would be the best way of doing something like this? I was thinking scatter, but when I do this the columns are spread out too far. I would like to get something much like the chart from the link I provided where the columns are as close to one another as possible. Is there a parameter I can overwrite to force this? First time using matplotlib so please excuse me if this is trivial. Thanks.
You can adjust the size of the symbols used in a scatter plot by choosing the s parameter. You also will likely need to adjust the size of your figure (with figsize) or the dimensions of your axes (with add_axes). This is because the symbols for scatter are square, in display units, and the x and y axis are not automatically adjusted so that width-of-one-change = height-of-one-box.
In other words, the example you provided is a rectangular plot with the height > width, and the height and width are chosen to make the width-of-one-change == height-of-one-box.
Here's an example of apply these techniques:
import matplotlib.pyplot as plt
BOX = 5
START = 365
changes = (8, -3, 4, -4, 12, -3, 7, -3, 5, -9, 3)
# one way to force dimensions is to set the figure size:
fig = plt.figure(figsize=(5, 10))
# another way is to control the axes dimensions
# for axes to have specific dimensions:
# [ x0, y0, w, h] in figure units, from 0 to 1
#ax = fig.add_axes([.15, .15, .7*.5, .7])
ax = fig.add_axes([.15, .15, .7, .7])
def sign(val):
return val / abs(val)
pointChanges = []
for chg in changes:
pointChanges += [sign(chg)] * abs(chg)
symbol = {-1:'o',
1:'x'}
chgStart = START
for ichg, chg in enumerate(changes):
x = [ichg+1] * abs(chg)
y = [chgStart + i * BOX * sign(chg) for i in range(abs(chg))]
chgStart += BOX * sign(chg) * (abs(chg)-2)
ax.scatter(x, y,
marker=symbol[sign(chg)],
s=175) #<----- control size of scatter symbol
ax.set_xlim(0, len(changes)+1)
fig.savefig('pointandfigure.png')
plt.show()
The method developed for each scatter plot is very hackish, but the key point is that I needed to play with the scatter s parameter and the figure size to get something of the desired effect.
The resulting plot is:
Ideally, one would make a custom method modeled after the scatter method. It would create a custom Collection instance that would include the x's, o's and month labels. It would also a) automatically adjust the axes/figure aspect or b) make asymmetric symbols. This is obviously an advanced option, intended for someone wishing to contribute, as a developer, to the Matplotlib project.
No personal experience, but maybe set_view_interval() or set_data_interval() from here? I have used Matplotlib for a project, but didn't have to play with fixing the x-axis width.
Editing the colors was the best I could.
chgStart = START
colors=['red','black','green','blue']
for ichg, chg in enumerate(changes):
x = [ichg+1] * abs(chg)
y = [chgStart + i * BOX * sign(chg) for i in range(abs(chg))]
chgStart += BOX * sign(chg) * (abs(chg)-2)
ax.scatter(x, y,
marker=symbol[sign(chg)],
s=175, color = colors[int(sign(chg))] ) #<----- control size of scatter symbol