Controlling the number of significant digits in a pyplot-label - python

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()

Related

Show dates in xticks only where value exist in plot chart of multiple dataframes

I have got a matplotlib question about xticks. I wanted to hide all those values that do not occur. I actually did it, but for the second set of values (red chart). I found how to hide for a specific data frame but not for 2 or more.
This is my code:
plt.subplots(figsize=(2, 1), dpi=400)
width = 0.005
xlim = np.arange(0, 1, 0.01)
ylim = np.arange(0, 0.1, 0.001)
plt.xticks(density_2.index.unique(), rotation=90, fontsize=1.5)
plt.yticks(density_2.unique(), fontsize=2)
plt.bar(density_1.index, density_1, width, color='Green', label=condition_1,alpha=0.5)
plt.bar(density_2.index, density_2, width, color='Red', label=condition_2,alpha=0.5)
plt.legend(loc="upper right", fontsize=2)
plt.show()
Link where I saw the solution: show dates in xticks only where value exist in plot chart and hide unnecessary interpolated xtick labels
Thank you very much in advance!
You need to find the intersection of the two lists of density_1's and density_2's ticks, as reported here.
Working example:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
N = 150
values_1 = np.random.randint(low = 5, high = 75, size = N)/100
density_1 = pd.DataFrame({'density_1': values_1})
density_1 = density_1.value_counts().sort_index(ascending = True)
density_1.index = sorted(list(set(values_1)), reverse = False)
values_2 = np.random.randint(low = 35, high = 100, size = N)/100
density_2 = pd.DataFrame({'density_2': values_2})
density_2 = density_2.value_counts().sort_index(ascending = True)
density_2.index = sorted(list(set(values_2)), reverse = False)
width = 0.005
condition_1 = 'Adele'
condition_2 = 'Extremoduro'
fig, ax = plt.subplots(figsize = (10, 5))
ax.bar(density_1.index, density_1, width, color = 'Green', label = condition_1, alpha = 0.5)
ax.bar(density_2.index, density_2, width, color = 'Red', label = condition_2, alpha = 0.5)
ax.legend(loc = 'upper right')
ax.set_xticks(list(set(density_1.index.unique()) & set(density_2.index.unique())), rotation = 90)
plt.show()
In the line:
list(set(density_1.index.unique()) & set(density_2.index.unique()))
you can select ticks which blongs to both density_1 and density_2.
Zoom in:

How fit second label bottom of the first label matplolib python

I would like to put specific marker like second label bottom of the first label in a plot of matplotlib.
The format of my files is like this:
File 1.txt
3
4
6
.
.
etc
file 2.txt
5
12
8
.
.
etc
file 3.txt
230.45
345.65
342.3
.
.
etc.
My script is this:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from numpy import *
from matplotlib.ticker import FormatStrFormatter
from matplotlib.ticker import MaxNLocator
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as tkr
import matplotlib.patches as patches
with open("1.txt") as f:
lstx = [int(x) for x in f.read().split()]
with open("2.txt") as f:
lsty = [int(x) for x in f.read().split()]
with open("3.txt") as f:
lstz = [float(x) for x in f.read().split()]
def numfmt(x, pos):
s = '{}'.format(int(x + 120))
return s
def numfmty(y, pos):
m = '{}'.format(int(y + 120))
return m
x=np.array(lstx)
y=np.array(lsty)
z=np.array(lstz)
df = pd.DataFrame.from_dict(np.array([y,x,z]).T)
df.columns = ['X_value','Y_value','Z_value']
df['Z_value'] = pd.to_numeric(df['Z_value'])
fig, ax = plt.subplots(figsize=(11,9))
pivotted= df.pivot('X_value','Y_value','Z_value')
ax = sns.heatmap(pivotted, cmap='plasma_r', vmin=0.0, vmax=234.525)
cbar = ax.collections[0].colorbar
cbar.ax.tick_params(labelsize=20)
plt.gca().invert_yaxis()
xfmt = tkr.FuncFormatter(numfmt)
plt.gca().xaxis.set_major_formatter(xfmt)
yfmt = tkr.FuncFormatter(numfmty)
plt.gca().yaxis.set_major_formatter(yfmt)
plt.xlabel('\n Number', fontsize=24)
plt.ylabel('Number \n', fontsize=24)
plt.xticks(size=16)
plt.yticks(size=16)
plt.tight_layout()
major_ticks = np.arange(0, 33, 1)
minor_ticks = np.arange(0, 33, 1)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, minor=True)
ax.set_yticks(major_ticks)
ax.set_yticks(minor_ticks, minor=True)
ax.grid(which='both')
ax.grid(which='minor', alpha=0.5)
ax.grid(which='major', alpha=0.5)
rect3 = patches.Rectangle((5,5),13,13,linewidth=1.7,linestyle='--',edgecolor='black',facecolor='none')
ax2 = ax.twiny()
ax2.xaxis.set_ticks_position("bottom")
ax2.xaxis.set_label_position("bottom")
newpos=[2,4,6]
newlabel=['*', '*', '*']
ax2.set_xticks(newpos)
ax2.set_xticklabels(newlabel)
ax.add_patch(rect3)
plt.grid()
plt.show()
I would like to put a marker '*' in the positions 125, 128, 130, 133, 138, 142 and 143 in both axis, with a size of 16.
When I try to put them, these are very small, are up of the first label and the grid is move it. The output is this:
How can I fit that? Thanks a lot
The following code adds stars at the indicated columns and rows.
Something confusing about seaborn is that it is quite opinionated giving priority to how formatting looks like and not caring too much about the internal representation. For example, the real tick positions are at the halves, but shown as integers.
Note that plt.tight_layout() is preferably one of the last commands just before plt.show(). Also note that if you created the ax beforehand, it is recommended to pass it as a parameter to sns.heatmap().
In the code below, the major ticks are at the halves to position the tick labels, while the minor ticks are at the integer positions to show the grid.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np
import pandas as pd
import seaborn as sns
x = np.tile(np.arange(0, 33, dtype=int), 33)
y = np.repeat(np.arange(0, 33, dtype=int), 33)
z = np.random.randint(0, 50, len(x)).astype(float)
z[x == y] = np.nan
z[np.abs(x - y) == 1] = 200
z[np.abs(x - y) == 2] = 150
df = pd.DataFrame.from_dict({'X_value': x, 'Y_value': y, 'Z_value': z})
pivotted = df.pivot('X_value', 'Y_value', 'Z_value')
fig, ax = plt.subplots(figsize=(11, 9))
sns.heatmap(pivotted, cmap='plasma_r', vmin=0.0, vmax=234.525, square=True, ax=ax)
ax.invert_yaxis()
cbar = ax.collections[0].colorbar
cbar.ax.tick_params(labelsize=20)
ax.set_xlabel('\n Number', fontsize=24)
ax.set_ylabel('Number \n', fontsize=24)
major_tick_pos = np.arange(0.5, 33, 1)
special_ticks = [125, 128, 130, 133, 138, 142, 143]
major_tick_labels = [('★ ' if i + 120 in special_ticks else '') + f'{i + 120}' for i in range(33)]
minor_tick_pos = np.arange(0, 34, 1)
ax.set_xticks(major_tick_pos)
ax.set_xticks(minor_tick_pos, minor=True)
ax.set_xticklabels(major_tick_labels, size=16, rotation=90)
ax.set_yticks(major_tick_pos)
ax.set_yticks(minor_tick_pos, minor=True)
ax.set_yticklabels(major_tick_labels, size=16, rotation=0)
ax.grid(which='minor', color='black', ls=':', alpha=0.5, lw=2)
ax.tick_params(axis='both', length=0)
rect3 = patches.Rectangle((5, 5), 13, 13, linewidth=1.7, linestyle='--', edgecolor='black', facecolor='none')
ax.add_patch(rect3)
plt.tight_layout()
plt.show()
PS: If you'd like the stars at the other side of the grid, both twinx() and twiny() are needed, only using the '★ ' if i + 120 in special_ticks else '' part of the labels.
An alternative idea would be to use annotations inside the cells to mark the special rows and columns:
stars = [['☆' if x in special_ticks or y in special_ticks else '' for x in range(120, 153)]
for y in range(120, 153)]
sns.heatmap(pivotted, cmap='plasma_r', vmin=0.0, vmax=234.525,
annot=stars, fmt='s', annot_kws={'size':20}, square=True, ax=ax)
To change the tick label colors, an approach could be:
xticks = ax.set_xticklabels(major_tick_labels, size=16, rotation=90)
yticks = ax.set_yticklabels(major_tick_labels, size=16, rotation=0)
for t in xticks + yticks:
if t.get_text().startswith('★'):
t.set_color('crimson')

How to move the position of the second x axis?

I am trying to create a graph with a secondary x-axis however I want the label and the ticks of the secondary x-axis to lie under the first. I have currently only found methods to move it to the bottom and not to an exact position. I have attached an image of what I am trying to achieve.
y = [3, 5, 2, 8, 7]
x = [[10, 11, 12, 13, 14], [36, 39.6, 43.2, 46.8, 50.4]]
labels = ['m/s', 'km/hr']
fig,ax = plt.subplots()
ax.plot(x[0], y)
ax.set_xlabel("Velocity m/s")
ax.set_ylabel("Time /mins")
ax2=ax.twiny()
ax2.plot(x[1], y)
ax2.set_xlabel("Velocity km/hr")
plt.show()
Answer
Firstly you have to include the required libraries:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
then you could generate the first axis with
ax = host_subplot(111, axes_class = AA.Axes, figure = fig)
then generate the secondary axis by
ax2=ax.twiny()
At this point you need to make some space for the secondary axis, therefore you should raise the bottom of the plot area with
plt.subplots_adjust(bottom = 0.2)
and finally position the secondary axis under the first one by
offset = -40
new_fixed_axis = ax2.get_grid_helper().new_fixed_axis
ax2.axis['bottom'] = new_fixed_axis(loc = 'bottom',
axes = ax2,
offset = (0, offset))
ax2.axis['bottom'].toggle(all = True)
Whole code
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
y = [3, 5, 2, 8, 7]
x = [[10, 11, 12, 13, 14], [36, 39.6, 43.2, 46.8, 50.4]]
labels = ['m/s', 'km/hr']
fig = plt.figure()
# generate the first axis
ax = host_subplot(111, axes_class = AA.Axes, figure = fig)
ax.plot(x[0], y)
ax.set_xlabel("Velocity m/s")
ax.set_ylabel("Time /mins")
ax2=ax.twiny()
# make space for the secondary axis
plt.subplots_adjust(bottom = 0.2)
# set position ax2 axis
offset = -40
new_fixed_axis = ax2.get_grid_helper().new_fixed_axis
ax2.axis['bottom'] = new_fixed_axis(loc = 'bottom',
axes = ax2,
offset = (0, offset))
ax2.axis['bottom'].toggle(all = True)
ax2.plot(x[1], y)
ax2.set_xlabel("Velocity km/hr")
plt.show()
Result

Matplotlib: Getting different colors in data lines with error bars

I am trying to draw two data lines with error bars, each having the same color as the data line. However, I get another thin line with a color I have not specified in each data line when I add an error bar.
Also, I would like to make the caps of the error bars thicker but the option capthick is not valid here.
Could anybody please help me fix these issues?
This is my code.
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
xaxis = [1, 2, 3]
mean1 = [1,2,3.6]
se1 = [0.2, 0.5, 0.9]
mean2 = [10, 29, 14]
se2 = [3, 4, 2]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlabel('X', fontsize = 16)
ax.set_ylabel('Y', fontsize = 16)
ax.axis([0, 5, 0, 35])
ax.plot(xaxis, mean1, 'r--', linewidth = 4)
ax.errorbar(xaxis, mean1, yerr = se1, ecolor = 'r', elinewidth = 2, capsize = 5)
ax.plot(xaxis, mean2, 'b--', linewidth = 4)
ax.errorbar(xaxis, mean2, yerr = se2, ecolor = 'b', elinewidth = 2, capsize = 5)
plt.show()
The extra thin line is coming from the errorbar() call.
errorbar will draw a line too, what you're doing is changing the colour of the error bars, but not the actual lines (hence it using the standard matplotlib first two colours, blue and green.
it's all in the documentaion, here.
To achieve what you want, you only need to use the errorbar() function;
This does what you want i think, maybe jsut tweak the numbers a bit.
import matplotlib.pyplot as plt
from pylab import *
import numpy as np
xaxis = [1, 2, 3]
mean1 = [1,2,3.6]
se1 = [0.2, 0.5, 0.9]
mean2 = [10, 29, 14]
se2 = [3, 4, 2]
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_xlabel('X', fontsize = 16)
ax.set_ylabel('Y', fontsize = 16)
ax.axis([0, 5, 0, 35])
linestyle = {"linestyle":"--", "linewidth":4, "markeredgewidth":5, "elinewidth":5, "capsize":10}
ax.errorbar(xaxis, mean1, yerr = se1, color="r", **linestyle)
ax.errorbar(xaxis, mean2, yerr = se2, color="b", **linestyle)
plt.show()
I put the common line style arguments into a dict which gets unpacked.

How to plot bar graphs with same X coordinates side by side ('dodged')

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...

Categories