For this simple plot I want to enlarge the figure size but I want to keep the actual plot size. How is this possible? Until now I found just a lot of possibilities which changed both sizes together.
import matplotlib.pyplot as plt
plt.plot([-1, -4.5, 16, 23, 15, 59])
plt.show()
You can achieve a constant axes sizes by addind the axes manually.
In my code example I introduce an scale factor sc which determines the ratio of figure and axes size.
import matplotlib.pyplot as plt
gr = (1 + np.sqrt(5))/2
sc = 2
fig_w = 3 * gr * sc
fig_h = 3 * sc
fig = plt.figure(figsize=(fig_w, fig_h))
panel_width = 1/sc
panel_height = 1/sc
off = (1 - 1/sc) / 2
ax = fig.add_axes([off, off, panel_width, panel_height])
ax.plot([-1, -4.5, 16, 23, 15, 59])
Related
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import figure
plt.style.use('ggplot')
overs = np.arange(1, 51)
india_score = np.random.randint(low = 1, high = 18, size = 50, dtype = 'int16')
plt.bar(overs, india_score, width = 0.80, align = 'center', color = 'orange', label = 'Runs per over')
plt.xlabel('Overs')
plt.ylabel('Score')
plt.title('India Inning')
plt.axis([1, 50, 0, 18])
plt.legend()
plt.grid(color='k', linestyle='-', linewidth=1)
fig = plt.gcf()
fig.set_size_inches(16, 9)
plt.show()
The output looks like this:
If you see the bar chart then runs scored in first over and runs scored in last over stick to the Y axis. How can I give some space between Y axis and my first and last vertical bars. I tried the margins function but that is not working
I searched for similar posts but I was unable to understand the solution as I am new to matplotlib. Any help will be greatly appreciated. Thanks.
Here is how you could do this:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.pyplot import figure
plt.style.use('ggplot')
overs = np.arange(1, 51)
india_score = np.random.randint(low = 1, high = 18, size = 50, dtype = 'int16')
plt.bar(overs, india_score, width = 0.80, align = 'center', color = 'orange', label = 'Runs per over')
plt.xlabel('Overs')
plt.ylabel('Score')
plt.title('India Inning')
plt.axis([1, 50, 0, 18])
plt.legend()
plt.grid(color='k', linestyle='-', linewidth=1)
fig = plt.gcf()
fig.set_size_inches(16, 9)
left, right = plt.xlim()
plt.xlim(left-1, right+1)
plt.show()
left, right = plt.xlim() gets the current limits of the x-axis and plt.xlim(left-1, right+1) sets the new limits by one step further outside relative to the old limits.
I have a stacked histogram made using matplotlib. It has of course multiple bins (on per sector) and each bin/bar is further segmented in subsectors (stacked histogram).
I'm wondering how I could get the datapoints, do some math (let's say divide each bin by it's total value), and than set the new datapoints.
How I expect it to work:
import matplotlib.plt as plt
ax = plt.subplt(111)
h = ax.hist((subsector1,subsector2,subsector3), bins = 20, stacked=True)
y_data = h.get_yData
The shape of y_data would be something like 20 x 3 (bins x subsectors)
new_y_data = y_data normalized by total on each bin
The shape of new_y_data would also be like 20 x 3, but the sum on each bin would be 1 (or 100%)
new_h = h.set_yData(new_y_data)
new_h would look more like a bar plot, with equal sized bars, but different subsector distributions on each bar..
Is this even possible in python matplotlib?
When you only want the values, it's easier to use np.histogram which does the same calculations without the need to draw.
When you have values, plt.bar draws the directly without needing plt.hist.
Pandas plot.bar might be an alternative. Have a look at Creating percentage stacked bar chart using groupby for an example similar to yours.
Here is some example code using np.histogram and plt.bar:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
subsector1 = np.clip(np.random.normal(70, 20, 400), 0, 100)
subsector2 = np.clip(np.random.normal(50, 20, 1000), 0, 100)
subsector3 = np.clip(np.random.normal(25, 20, 500), 0, 100)
num_bins = 20
x_min = np.min(np.concatenate([subsector1, subsector2, subsector3]))
x_max = np.max(np.concatenate([subsector1, subsector2, subsector3]))
bounds = np.linspace(x_min, x_max, num_bins + 1)
values = np.zeros((num_bins, 3))
for i, subsect in enumerate((subsector1, subsector2, subsector3)):
values[:, i], _ = np.histogram(subsect, bins=bounds)
with np.errstate(divide='ignore', invalid='ignore'):
values /= values.sum(axis=1, keepdims=True)
fig, ax = plt.subplots()
bottom = 0
for i in range(3):
plt.bar((bounds[:-1] + bounds[1:]) / 2, values[:, i], bottom=bottom, width=np.diff(bounds) * 0.8)
bottom += values[:, i]
plt.xlim(x_min, x_max)
plt.gca().yaxis.set_major_formatter(PercentFormatter(1.0))
plt.show()
I wish to produce a single line plot in Matplotlib that has variable transparency, i.e. it starts from solid color to full transparent color.
I tried this but it didn't work.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 500)
y = np.sin(x)
alphas = np.linspace(1, 0, 500)
fig, ax = plt.subplots(1, 1)
ax.plot(x, y, alpha=alphas)
Matplotlib's "LineCollection" allows you to split the line to be plotted into individual line segments and you can assign a color to each segment. The code example below shows how each horizontal "x" value can be assigned an alpha (transparency) value that indexes into a sequential colormap that runs from transparent to a given color. A suitable colormap "myred" was created using Matplotlib's "colors" module.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import matplotlib.colors as colors
redfade = colors.to_rgb("red") + (0.0,)
myred = colors.LinearSegmentedColormap.from_list('my',[redfade, "red"])
x = np.linspace(0,1, 1000)
y = np.sin(x * 4 * np.pi)
alphas = x * 4 % 1
points = np.vstack((x, y)).T.reshape(-1, 1, 2)
segments = np.hstack((points[:-1], points[1:]))
fig, ax = plt.subplots()
lc = LineCollection(segments, array=alphas, cmap=myred, lw=3)
line = ax.add_collection(lc)
ax.autoscale()
plt.show()
If you are using the standard white background then you can save a few lines by using one of Matplotlib's builtin sequential colormaps that runs from white to a given color. If you remove the lines that created the colormap above and just put the agument cmap="Reds" in the LineCollection function, it creates a visually similar result.
The only solution I found was to plot each segment independently with varying transparency
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 500)
y = np.sin(x)
alphas = np.linspace(1, 0, 499)
fig, ax = plt.subplots(1, 1)
for i in range(499):
ax.plot(x[i:i+2], y[i:i+2], 'k', alpha=alphas[i])
But I don't like it... Maybe this is enough for someone
I don't know how to do this in matplotlib, but it's possible in Altair:
import numpy as np
import pandas as pd
import altair as alt
x = np.linspace(0, 2 * np.pi, 500)
y = np.sin(x)
alt.Chart(
pd.DataFrame({"x": x, "y": y, "o": np.linspace(0, 1, len(x))}),
).mark_point(
).encode(
alt.X("x"),
alt.Y("y"),
alt.Opacity(field="x", type="quantitative", scale=alt.Scale(range=[1, 0]), legend=None),
)
Result:
I've got wind data which includes wind speed and wind direction.
However, my wind direction is defined anti-clockwise. Meaning, 45 deg for my data is actually NW.
Is there any chance to change this using Windrose in Python?
I've got the following code to plot the Windrose:
from windrose import WindroseAxes
import matplotlib.pyplot as plt
theta = [0, 60, 120, 180, 240, 300]
speed = [10, 0, 10, 40, 50, 40]
ax = WindroseAxes.from_ax()
ax.bar(theta, speed)
plt.show()
The direction of your windrose is determined by the theta list. If 90° is not on the side you wish, you can convert all theta angles to the opposite and therefore create a mirror of your original image.
Let's imagine your original code is the following.
from windrose import WindroseAxes
import matplotlib.pyplot as plt
theta = [0, 90]
speed = [10, 10]
ax = WindroseAxes.from_ax()
ax.bar(theta, speed)
plt.show()
And this shows you a graph with a bar on the East, while you want it on the West (or the opposite).
If you take the opposite angle, you swap the graph. The following code would server your purpose.
from windrose import WindroseAxes
import matplotlib.pyplot as plt
theta = [0, 90]
theta = [360 - x for x in theta] # Take the opposite angle
speed = [10, 10]
ax = WindroseAxes.from_ax()
ax.bar(theta, speed)
plt.show()
I'm having a problem adding a colorbar to a plot of many lines corresponding to a power-law.
To create the color-bar for a non-image plot, I added a dummy plot (from answers here: Matplotlib - add colorbar to a sequence of line plots).
To colorbar ticks do not correspond to the colors of the plot.
I have tried changing the norm of the colorbar, and I can fine-tune it to be semy accurate for a particular case, but I can't do that generally.
def plot_loglog_gauss():
from matplotlib import cm as color_map
import matplotlib as mpl
"""Creating the data"""
time_vector = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
amplitudes = [t ** 2 * np.exp(-t * np.power(np.linspace(-0.5, 0.5, 100), 2)) for t in time_vector]
"""Getting the non-zero minimum of the data"""
data = np.concatenate(amplitudes).ravel()
data_min = np.min(data[np.nonzero(data)])
"""Creating K-space data"""
k_vector = np.linspace(0,1,100)
"""Plotting"""
number_of_plots = len(time_vector)
color_map_name = 'jet'
my_map = color_map.get_cmap(color_map_name)
colors = my_map(np.linspace(0, 1, number_of_plots, endpoint=True))
# plt.figure()
# dummy_plot = plt.contourf([[0, 0], [0, 0]], time_vector, cmap=my_map)
# plt.clf()
norm = mpl.colors.Normalize(vmin=time_vector[0], vmax=time_vector[-1])
cmap = mpl.cm.ScalarMappable(norm=norm, cmap=color_map_name)
cmap.set_array([])
for i in range(number_of_plots):
plt.plot(k_vector, amplitudes[i], color=colors[i], label=time_vector[i])
c = np.arange(1, number_of_plots + 1)
plt.xlabel('Frequency')
plt.ylabel('Amplitude')
plt.yscale('symlog', linthreshy=data_min)
plt.xscale('log')
plt.legend(loc=3)
ticks = time_vector
plt.colorbar(cmap, ticks=ticks, shrink=1.0, fraction=0.1, pad=0)
plt.show()
By comparing with the legend you see the ticks values don't match the actual colors. For example, 128 is shown in green in the colormap while red in the legend.
The actual result should be a linear-color colorbar. with ticks at regular intervals on the colorbar (corresponding to irregular time intervals...). And of course correct color for value of tick.
(Eventually the plot contains many plots (len(time_vector) ~ 100), I lowered the number of plots to illustrate and to be able to show the legend.)
To clarify, this is what I want the result to look like.
The most important principle is to keep the colors from the line plots and the ScalarMappable in sync. This means, the color of the line should not be taken from an independent list of colors, but rather from the same colormap and using the same normalization as the colorbar to be shown.
One major problem is then to decide what to do with 0 which cannot be part of a loagrithmic normalization. The following is a workaround assuming a linear scale between 0 and 2, and a log scale above, using a SymLogNorm.
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
"""Creating the data"""
time_vector = [0, 1, 2, 4, 8, 16, 32, 64, 128, 256]
amplitudes = [t ** 2 * np.exp(-t * np.power(np.linspace(-0.5, 0.5, 100), 2)) for t in time_vector]
"""Getting the non-zero minimum of the data"""
data = np.concatenate(amplitudes).ravel()
data_min = np.min(data[np.nonzero(data)])
"""Creating K-space data"""
k_vector = np.linspace(0,1,100)
"""Plotting"""
cmap = plt.cm.get_cmap("jet")
norm = mpl.colors.SymLogNorm(2, vmin=time_vector[0], vmax=time_vector[-1])
sm = mpl.cm.ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])
for i in range(len(time_vector)):
plt.plot(k_vector, amplitudes[i], color=cmap(norm(time_vector[i])), label=time_vector[i])
#c = np.arange(1, number_of_plots + 1)
plt.xlabel('Frequency')
plt.ylabel('Amplitude')
plt.yscale('symlog', linthreshy=data_min)
plt.xscale('log')
plt.legend(loc=3)
cbar = plt.colorbar(sm, ticks=time_vector, format=mpl.ticker.ScalarFormatter(),
shrink=1.0, fraction=0.1, pad=0)
plt.show()