Related
If I've got a plot with some data points, where I have calculated a certain χ^2_min value, and I want that value to be displayed, how do I indicate how many significant digits are to be shown?
For example, I made this random script to test it out:
import numpy as np
import matplotlib.pyplot as plt
xs = np.array([1.0, 5.0, 10, 20, 50])
ys = np.array([10, 50, 200, 250, 300])
y_err = 20
chi2min = 123
fig, ax = plt.subplots()
ax.errorbar(xs, ys, y_err, color = 'tab:blue', fmt = 'o', ms = 5, capsize=4, label = r'$\chi^2_{min}$ = 'f'{chi2min:.1f}')
ax.legend()
The f-thing, we were tought in class, apparently only controls the number of decimals - how do I round down to 2 digits and make python display χ^2_min = 1.2e+01?
EDIT: I've found out how to do it with one number, but what if I've got multiple values, I want to be shown with two significant digits in a loop? Fx:
import numpy as np
import matplotlib.pyplot as plt
xs = np.array([1.0, 5.0, 10, 20, 50])
ys = np.array([10, 50, 200, 250, 300])
y_err = 20
chi2mins = [123, 80]
fig, axs = plt.subplots(2, 1, figsize = (12, 12))
for ax, chi2min in zip(axs, chi2mins):
ax.errorbar(xs, ys, y_err, color = 'tab:blue', fmt = 'o', ms = 5, capsize=4, label = r'$\chi^2_{min}$ = 'f'{chi2min:.1f}')
ax.legend()
How do I round down to 2 digits and make python display χ^2_min = 1.2e+02 on the first plot and χ^2_min = 80 on the second?
Replace:
label = r'$\chi^2_{min}$ = 'f'{chi2min:.1f}'
With:
label = r'$\chi^2_{min}$ = 'f'{0.1*chi2min:.1e}'
Update
...
for ax, chi2min in zip(axs, chi2mins):
chi2min = f'{chi2min:.1e}' if isinstance(chi2min, float) or chi2min >= 100 else str(chi2min)
ax.errorbar(xs, ys, y_err, color = 'tab:blue', fmt = 'o', ms = 5, capsize=4, label = r'$\chi^2_{min}$ = 'f'{chi2min}')
ax.legend()
I want to combine multiple grouped bar charts into one figure, as the image below shows.
grouped bar charts in a single figure
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
Firstly I created each grouped bar chart by plt.subplots()
x = np.arange(len(labels))
width = 0.3
fig1, ax = plt.subplots()
rects1 = ax.bar(x-width/2, yesterday_test1_mean, width)
rects2 = ax.bar(x+width/2, yesterday_test2_mean, width)
fig2, ax = plt.subplots()
rects3 = ax.bar(x-width/2, today_test1_mean, width)
rects4 = ax.bar(x+width/2, today_test2_mean, width)
Then, I used add_subplot in an attempt to treat fig1 and fig2 as new axes in a new figure.
fig_all = plt.figure()
fig1 = fig_all.add_subplot(1,2,1)
fig2 = fig_all.add_subplot(1,2,2)
fig_all.tight_layout()
plt.show()
But it didn't work. How can I combined several grouped bar charts into a single figure?
Thanks in advance.
Well, I tried something. Here's a rough result. Only thing I changed is that rather using axes, I am just using subplot as I learned over time. So with fig and axes as output, there must be a way too. But this is all I've ever used. I've not added the legend and title yet, but I guess you can try it on your own too.
Here's the code with just small change:
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.bar(x-width/2, yesterday_test1_mean, width)
plt.bar(x+width/2, yesterday_test2_mean, width)
plt.subplot(122)
plt.bar(x-width/2, today_test1_mean, width)
plt.bar(x+width/2, today_test2_mean, width)
plt.show()
And here's your initial result:
While you see the result and try some stuff on your own, let me try to add the labels and legend to it as well as you've provided in the sample image.
Edit: The final output
So here it is, the exact thing you're looking for:
Code:
import matplotlib.pyplot as plt
import numpy as np
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
plt.figure(figsize=(12,5))
plt.subplot(121)
plt.title('Yesterday', fontsize=18)
plt.bar(x-width/2, yesterday_test1_mean, width, label='test1', hatch='//', color=np.array((199, 66, 92))/255)
plt.bar(x+width/2, yesterday_test2_mean, width, label='test2', color=np.array((240, 140, 58))/255)
plt.xticks([0,1,2], labels, fontsize=15)
plt.subplot(122)
plt.title('Today', fontsize=18)
plt.bar(x-width/2, today_test1_mean, width, hatch='//', color=np.array((199, 66, 92))/255)
plt.bar(x+width/2, today_test2_mean, width, color=np.array((240, 140, 58))/255)
plt.xticks([0,1,2], labels, fontsize=15)
plt.figlegend(loc='upper right', ncol=1, labelspacing=0.5, fontsize=14, bbox_to_anchor=(1.11, 0.9))
plt.tight_layout(w_pad=6)
plt.show()
There is two method for doing subplots that you might try to combine accidentally: plt.subplot and plt.subplots.
Here is example how you can use plt.subplots to create two bar charts.
import numpy as np
import matplotlib.pyplot as plt
labels = ['G1', 'G2', 'G3']
yesterday_test1_mean = [20, 12, 23]
yesterday_test2_mean = [21, 14, 25]
today_test1_mean = [18, 10, 12]
today_test2_mean = [13, 13, 9]
x = np.arange(len(labels))
width = 0.3
fig, (ax1, ax2) = plt.subplots(1, 2)
ax1.bar(x-width/2, yesterday_test1_mean, width)
ax1.bar(x+width/2, yesterday_test2_mean, width)
ax2.bar(x-width/2, today_test1_mean, width)
ax2.bar(x+width/2, today_test2_mean, width)
plt.show()
There is only little changes on your code. You can add labels with:
ax1.set_title('Yesterday')
ax2.set_title('Today')
ax1.set_xticks(x)
ax1.set_xticklabels(labels)
ax2.set_xticks(x)
ax2.set_xticklabels(labels)
I'm trying to create a radar chart using Python / Matplotlib where measured data can be "played back" using matplotlib's built in animation module. I want the data points to move along their respective axes as the data set is traversed. I have problems reading the data and updating the chart, nor am I able to find an example of this.
I have attached a piece of code that should give you an idea of what I am trying to achieve:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from math import pi
class SubplotAnimation(animation.TimedAnimation):
def __init__(self, data):
self.data = data
fig = plt.figure()
ax = fig.add_subplot(111, projection='polar')
# Figure definition
cat = ['A', 'B', 'C', 'D', 'E']
values = [10, 10, 10, 10, 10]
N = len(cat)
x_as = [n / float(N) * 2 * pi for n in range(N)]
# Because our chart will be circular we need to append a copy of
# the first value of each list at the end of each list with data
values += values[:1]
x_as += x_as[:1]
plt.rc('axes', linewidth=0.5, edgecolor='#888888') # Set color of axes
# Create polar plot
ax = plt.subplot(111, projection='polar')
# Set clockwise rotation. That is:
ax.set_theta_offset(pi / 2)
ax.set_theta_direction(-1)
# Set position of y-labels
ax.set_rlabel_position(0)
# Set color and linestyle of grid
ax.xaxis.grid(True, color="#888888", linestyle='solid', linewidth=0.5)
ax.yaxis.grid(True, color="#888888", linestyle='solid', linewidth=0.5)
# Set number of radial axes and remove labels
plt.xticks(x_as[:-1], [])
# Set yticks
plt.yticks([20, 40, 60, 80, 100], ["20", "40", "60", "80", "100"])
# Set axes limits
plt.ylim(0, 100)
# Draw ytick labels to make sure they fit properly
for i in range(N):
angle_rad = i / float(N) * 2 * pi
if angle_rad == 0:
ha, distance_ax = "center", 10
elif 0 < angle_rad < pi:
ha, distance_ax = "left", 1
elif angle_rad == pi:
ha, distance_ax = "center", 1
else:
ha, distance_ax = "right", 1
ax.text(angle_rad, 100 + distance_ax, cat[i], size=10,
horizontalalignment=ha, verticalalignment="center")
animation.TimedAnimation.__init__(self, fig, interval=25, blit=True)
def new_frame_seq(self):
return iter(range(len(self.data)))
def _draw_frame(self, framedata):
ax.plot(ax, framedata)
testdata = [[10, 20, 30, 40, 50],
[10, 20, 30, 40, 50],
[40, 50, 60, 70, 80],
[40, 50, 60, 70, 80],
[50, 60, 70, 80, 90]]
ani = SubplotAnimation(testdata)
plt.show()
Any tips on how to make this work will be greatly appreciated!
It's not clear what the aim of subclassing TimedAnimation would be. It makes things much too complicated.
Here is a simple example of an animated radar plot using FuncAnimation.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111, projection='polar')
ax.set_ylim(0,100)
data = np.random.rand(50)*6+2
theta = np.linspace(0,2.*np.pi, num=50)
l, = ax.plot([],[])
def update(i):
global data
data += (np.random.rand(50)+np.cos(i*2.*np.pi/50.))*2
data[-1] = data[0]
l.set_data(theta, data )
return l,
ani = animation.FuncAnimation(fig, update, frames=50, interval=200, blit=True)
plt.show()
I have a list of x and a list of y values. I'd like to construct a scatterplot in Matplotlib and divide the dots into five categories based on their x and y coordinates, like in the image below:
angles = [0, 18, 36, 54, 72, 90]
colors = ['r','g','b','c']
x = [....]
y = [....]
All of the points in the divided category will be the same color. It would also be great to have a legend for the categories. I am new to Matplotlib and Python, does anyone know how I can approach this?
Here's a working example which will give you a little idea to get started:
from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import math
import random
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
w, h = 7, 5
a = [[random.random() * w, random.random() * h] for i in range(100)]
plt.plot(*zip(*a), marker='o', color='r', ls='')
for deg in [18, 36, 54]:
r = 10
line = Line2D([0, r * math.cos(math.radians(deg))],
[0, r * math.sin(math.radians(deg))],
linewidth=1, linestyle="-", color="green")
ax.add_line(line)
ax.set_xlim(0, w)
ax.set_ylim(0, h)
plt.legend()
plt.show()
import matplotlib.pyplot as plt
gridnumber = range(1,4)
b1 = plt.bar(gridnumber, [0.2, 0.3, 0.1], width=0.4,
label="Bar 1", align="center")
b2 = plt.bar(gridnumber, [0.3, 0.2, 0.2], color="red", width=0.4,
label="Bar 2", align="center")
plt.ylim([0,0.5])
plt.xlim([0,4])
plt.xticks(gridnumber)
plt.legend()
plt.show()
Currently b1 and b2 overlap each other. How do I plot them separately like so:
There is an example in the matplotlib site. Basically, you just shift the x values by width. Here is the relevant bit:
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
menStd = (2, 3, 4, 1, 2)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars
fig = plt.figure()
ax = fig.add_subplot(111)
rects1 = ax.bar(ind, menMeans, width, color='royalblue', yerr=menStd)
womenMeans = (25, 32, 34, 20, 25)
womenStd = (3, 5, 2, 3, 3)
rects2 = ax.bar(ind+width, womenMeans, width, color='seagreen', yerr=womenStd)
# add some
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(ind + width / 2)
ax.set_xticklabels( ('G1', 'G2', 'G3', 'G4', 'G5') )
ax.legend( (rects1[0], rects2[0]), ('Men', 'Women') )
plt.show()
Below answer will explain each and every line of code in the simplest manner possible:
# Numbers of pairs of bars you want
N = 3
# Data on X-axis
# Specify the values of blue bars (height)
blue_bar = (23, 25, 17)
# Specify the values of orange bars (height)
orange_bar = (19, 18, 14)
# Position of bars on x-axis
ind = np.arange(N)
# Figure size
plt.figure(figsize=(10,5))
# Width of a bar
width = 0.3
# Plotting
plt.bar(ind, blue_bar , width, label='Blue bar label')
plt.bar(ind + width, orange_bar, width, label='Orange bar label')
plt.xlabel('Here goes x-axis label')
plt.ylabel('Here goes y-axis label')
plt.title('Here goes title of the plot')
# xticks()
# First argument - A list of positions at which ticks should be placed
# Second argument - A list of labels to place at the given locations
plt.xticks(ind + width / 2, ('Xtick1', 'Xtick3', 'Xtick3'))
# Finding the best position for legends and putting it
plt.legend(loc='best')
plt.show()
Sometimes could be tricky to find the right bar width. I usually use this np.diff to find the right dimension.
import numpy as np
import matplotlib.pyplot as plt
#The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = [5.5,6,7,8.5,8.9]
#Calculate optimal width
width = np.min(np.diff(indices))/3
fig = plt.figure()
ax = fig.add_subplot(111)
# matplotlib 3.0 you have to use align
ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin',align='edge')
ax.bar(indices,menMeans,width,color='r',label='Ymax',align='edge')
ax.set_xlabel('Test histogram')
plt.show()
# matplotlib 2.0 (you could avoid using align)
# ax.bar(indices-width,womenMeans,width,color='b',label='-Ymin')
# ax.bar(indices,menMeans,width,color='r',label='Ymax')
This is the result:
What if my indices on my x axis are nominal values like names:
#
import numpy as np
import matplotlib.pyplot as plt
# The data
womenMeans = (25, 32, 34, 20, 25)
menMeans = (20, 35, 30, 35, 27)
indices = range(len(womenMeans))
names = ['Asian','European','North Amercian','African','Austrailian','Martian']
# Calculate optimal width
width = np.min(np.diff(indices))/3.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.bar(indices-width/2.,womenMeans,width,color='b',label='-Ymin')
ax.bar(indices+width/2.,menMeans,width,color='r',label='Ymax')
#tiks = ax.get_xticks().tolist()
ax.axes.set_xticklabels(names)
ax.set_xlabel('Test histogram')
plt.show()
Here are two examples of creating a side-by-side bar chart when you have more than two "categories" in a group.
Manual Method
Manually set the position and width of each bar.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import ticker
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = np.array([.01, .05, .10, .25])
# Coin values times *n* coins
# This controls how many bars we get in each group
values = [worth*i for i in range(1,6)]
n = len(values) # Number of bars to plot
w = .15 # With of each column
x = np.arange(0, len(coins)) # Center position of group on x axis
for i, value in enumerate(values):
position = x + (w*(1-n)/2) + i*w
plt.bar(position, value, width=w, label=f'{i+1}x')
plt.xticks(x, coins);
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.legend()
Pandas Method
If you put the data into a pandas DataFrame, pandas will do the hard stuff for you.
import pandas as pd
coins = ['penny', 'nickle', 'dime', 'quarter']
worth = [0.01, 0.05, 0.10, 0.25]
df = pd.DataFrame(worth, columns=['1x'], index=coins)
df['2x'] = df['1x'] * 2
df['3x'] = df['1x'] * 3
df['4x'] = df['1x'] * 4
df['5x'] = df['1x'] * 5
from matplotlib import ticker
import matplotlib.pyplot as plt
df.plot(kind='bar')
plt.ylabel('Monetary Value')
plt.gca().yaxis.set_major_formatter(ticker.FormatStrFormatter('$%.2f'))
plt.gca().xaxis.set_tick_params(rotation=0)
Pandas creates a similar figure...