How fit second label bottom of the first label matplolib python - 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')

Related

Font and colour for Python matplotlib legend- superscript

Can someone help me make the R2 writing look like the rest? Especially not italics. This is my code below:
While I'm here can anyone tell me how to either
get that R2=0.97 line in red writing to indicate that's what the red line is on the graph
or
insert a red line/red dash within the legend on that line?
I've seen other ways of doing it online but the way I've formatted my legend doesn't allow for it.
plt.rcParams["font.family"] = "Cambria"
fig, ax = plt.subplots()
ax.scatter(y_test, y_predicted ,s=10,color='darkslateblue',linewidths=1)
ax.plot([y.min(), y.max()], [y.min(), y.max()], 'k-', lw=2,)
ax.set_xlabel('Actual (%)',fontsize='large')
ax.set_ylabel('Predicted (%)',fontsize='large')
y_test, y_predicted = y_test.reshape(-1,1), y_predicted.reshape(-1,1)
ax.plot(y_test, LinearRegression().fit(y_test, y_predicted).predict(y_test), color="red", lw=2)
ax.set_title('H2O REF')
handles = [mpl_patches.Rectangle((0, 0), 1, 1, fc="white", ec="white",
lw=0, alpha=0)] * 4
labels = []
labels.append("$R^2$ = {0:.2g}".format(Rsquared))
labels.append("RMSE = {0:.2g}".format(rmse))
labels.append("MAE = {0:.2g}".format(mae))
ax.legend(handles, labels, loc='best', fontsize='x-large',
fancybox=True, framealpha=0.7,
handlelength=0, handletextpad=0)
plt.show()
Thank you :)
For the first solution, a possible way to achieve it is by only typesetting ^2 in the math environment and than setting the first label text to red as described here, see code below.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpl_patches
x = np.linspace(0, 1)
y = x + np.random.normal(scale=0.1, size=50)
plt.rcParams["font.family"] = "Cambria"
Rsquared = 0.9
rmse = 0.8
mae = 1
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.plot(x, x, c='r')
handles = [mpl_patches.Rectangle((0, 0), 1, 1, fc="white", ec="white",
lw=0, alpha=0)] * 4
labels = []
labels.append("R$^2$ = {0:.2g}".format(Rsquared))
labels.append("RMSE = {0:.2g}".format(rmse))
labels.append("MAE = {0:.2g}".format(mae))
leg = ax.legend(handles, labels, loc='best', fontsize='x-large',
fancybox=True, framealpha=0.7,
handlelength=0, handletextpad=0)
texts = leg.get_texts()
texts[0].set_color("red")
Alternatively, you can create legend entries including the red line with Line2D.
The corresponding code down below overrides handles[0].
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpl_patches
from matplotlib.lines import Line2D
x = np.linspace(0, 1)
y = x + np.random.normal(scale=0.1, size=50)
plt.rcParams["font.family"] = "Cambria"
Rsquared = 0.9
rmse = 0.8
mae = 1
fig, ax = plt.subplots()
ax.scatter(x, y)
ax.plot(x, x, c='r')
handles = [mpl_patches.Rectangle((0, 0), 1, 1, fc="white", ec="white",
lw=0, alpha=0)] * 4
lines = []
handles[0] = Line2D([0], [0], color='red')
labels = []
labels.append("R$^2$ = {0:.2g}".format(Rsquared))
labels.append("RMSE = {0:.2g}".format(rmse))
labels.append("MAE = {0:.2g}".format(mae))
leg = ax.legend(handles, labels, loc='best', fontsize='x-large',
fancybox=True, framealpha=0.7)

Set custom and changing baseline to stem plot in Matplotlib

I'm triying to make a figure where the stem plot has the baseline on the data of dataframe_3_merged['TOTAL'].
import numpy as np
from eurostatapiclient import EurostatAPIClient
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import seaborn as sns
import pandas as pd
#Set versions and formats, so far only the ones used here are availeable and call client
VERSION = 'v2.1'
FORMAT = 'json'
LANGUAGE = 'en'
client = EurostatAPIClient(VERSION, FORMAT, LANGUAGE)
dataframe_3_query_total = 'ilc_peps01?precision=1&sex=T&geo=AT&geo=BE&geo=BG&geo=CH&geo=CY&geo=CZ&geo=DK&geo=EA19&geo=EE&geo=EL&geo=ES&geo=EU28&geo=FI&geo=FR&geo=HR&geo=HU&geo=IE&geo=IS&geo=IT&geo=LT&geo=LU&geo=LV&geo=ME&geo=MK&geo=MT&geo=NL&geo=NO&geo=PL&geo=PT&geo=RO&geo=RS&geo=SE&geo=SI&geo=SK&geo=TR&geo=UK&unit=PC&unitLabel=label&time=2018&age=TOTAL'
dataframe_3_query_urb = 'ilc_peps13?precision=1&deg_urb=DEG1&deg_urb=DEG2&deg_urb=DEG3&geo=AT&geo=BE&geo=BG&geo=CH&geo=CY&geo=CZ&geo=DE&geo=DK&geo=EA19&geo=EE&geo=EL&geo=ES&geo=EU28&geo=FI&geo=FR&geo=HR&geo=HU&geo=IE&geo=IS&geo=IT&geo=LT&geo=LU&geo=LV&geo=MK&geo=MT&geo=NL&geo=NO&geo=PL&geo=PT&geo=RO&geo=RS&geo=SE&geo=SI&geo=SK&geo=UK&unit=PC&unitLabel=label&time=2018'
dataframe_3_total = client.get_dataset(dataframe_3_query_total).to_dataframe().pivot(index = 'geo',columns = 'age',values = 'values')
dataframe_3_urb =client.get_dataset(dataframe_3_query_urb).to_dataframe().pivot(index = 'geo',columns = 'deg_urb',values = 'values')
dataframe_3_merged = dataframe_3_total.join(dataframe_3_urb).dropna()
fig, ax = plt.subplots(figsize=(15, 4))
plt.ylim(0,51)
x = range(0,32,1)
stem_1 =plt.stem(x,dataframe_3_merged['DEG1'])
stem_2=plt.stem(x, dataframe_3_merged['DEG2'])
stem_3=plt.stem(x, dataframe_3_merged['DEG3'])
plt.setp(stem_2, color = 'r')
plt.setp(stem_3, color = 'g')
scatterplot= sns.scatterplot(x=dataframe_3_merged.index, #We draw the scatterplot and specify the arguments
y = dataframe_3_merged['TOTAL'],
ax=ax ,
s = 100 ,
legend = False,
marker="_",
color = 'b')
The goal is to have a plot similar to this image:
I tried to use the list dataframe_3_merged['TOTAL'] as the parameter in the bottom argument of plt.stem but I have this traceback: ValueError: setting an array element with a sequence.
Thank you for your help!
You could replace each stem plot by a scatter plot and a plot of vertical lines (plt.vlines). Setting the zorder=0 ensures the lines are drawn behind the dots.
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
names = ['hydrogen', 'helium', 'lithium', 'beryllium', 'boron', 'carbon', 'nitrogen', 'oxygen', 'fluorine', 'neon', 'sodium', 'magnesium', 'aluminium', 'silicon', 'phosphorus', 'sulphur', 'chlorine', 'argon', 'potassium', 'calcium', 'scandium', 'titanium', 'vanadium', 'chromium', 'manganese', 'iron', 'cobalt', 'nickel', 'copper', 'zinc', 'gallium', 'germanium', 'arsenic', 'selenium', 'bromine', 'krypton']
N = len(names)
df = pd.DataFrame({'Deg1': 35 + np.random.normal(size=N).cumsum(),
'Deg2': 25 + np.random.normal(size=N).cumsum(),
'Deg3': 15 + np.random.normal(size=N).cumsum()},
index=names)
df['Total'] = df.mean(axis=1)
for deg, color, label in zip(['Deg1', 'Deg2', 'Deg3'], ['tomato', 'orange', 'palegreen'],
['label1', 'label2', 'label3']):
plt.vlines(df.index, df[deg], df['Total'], lw=0.2, color='k', zorder=0)
plt.scatter(df.index, df[deg], marker='o', color=color, label=label)
plt.scatter(df.index, df['Total'], marker='_', color='deepskyblue', s=100)
plt.xticks(rotation='vertical')
plt.ylim(0, 51)
plt.margins(x=0.02)
plt.legend(ncol=3, bbox_to_anchor=(0.5, -0.4), loc='upper center')
plt.grid(True, axis='y')
plt.tick_params(length=0)
for where in ['top', 'left', 'right']:
plt.gca().spines[where].set_visible(False)
plt.tight_layout()
plt.show()

How to plot zebra style axis in matplotlib

I want plot the axis in a zebra style similar to this:
Below is my code:
import matplotlib.pyplot as plt
import cartopy.io.shapereader as shpreader
import cartopy.crs as ccrs
from cartopy.feature import ShapelyFeature
fig, ax = plt.figure(figsize=(12,9), dpi=150 )
sFilename_shapefile = './some_shape.shp'
pShapeReader = shpreader.Reader(sFilename_shapefile)
pProjection_map = ccrs.PlateCarree()
aShapeFeature = ShapelyFeature(pShapeReader.geometries(),
pProjection_map, facecolor='grey', edgecolor='grey',
linewidth=0.5)
ax.add_feature(aShapeFeature, zorder = 4)
plt.show()
What I got is like this:
I've got a hacky solution that's working for my purposes:
The example usage:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
crs = ccrs.PlateCarree()
fig = plt.figure(figsize=(5, 2))
ax = fig.add_subplot(projection=crs)
ax.coastlines()
ax.set_extent((-125, -85, 22, 42))
ax.set_xticks((-120, -110, -100, -90))
ax.set_yticks((25, 30, 35, 40))
add_zebra_frame(ax, crs=crs)
I've put the frame in a function for now. It likely will not work for many polar-type projections that mix lat/lon ticks, and right now it doesn't work that well if you don't specify which tick marks you want (I'm still unclear how Cartopy picks the default ticks).
https://gist.github.com/scottstanie/dff0d597e636440fb60b3c5443f70cae
Basically all I'm doing is turning off the spines and plotting an alternating black/white line between each of the xticks/yticks.
import itertools
import matplotlib.patheffects as pe
import numpy as np
def add_zebra_frame(ax, lw=2, crs="pcarree", zorder=None):
ax.spines["geo"].set_visible(False)
left, right, bot, top = ax.get_extent()
# Alternate black and white line segments
bws = itertools.cycle(["k", "white"])
xticks = sorted([left, *ax.get_xticks(), right])
xticks = np.unique(np.array(xticks))
yticks = sorted([bot, *ax.get_yticks(), top])
yticks = np.unique(np.array(yticks))
for ticks, which in zip([xticks, yticks], ["lon", "lat"]):
for idx, (start, end) in enumerate(zip(ticks, ticks[1:])):
bw = next(bws)
if which == "lon":
xs = [[start, end], [start, end]]
ys = [[bot, bot], [top, top]]
else:
xs = [[left, left], [right, right]]
ys = [[start, end], [start, end]]
# For first and lastlines, used the "projecting" effect
capstyle = "butt" if idx not in (0, len(ticks) - 2) else "projecting"
for (xx, yy) in zip(xs, ys):
ax.plot(
xx,
yy,
color=bw,
linewidth=lw,
clip_on=False,
transform=crs,
zorder=zorder,
solid_capstyle=capstyle,
# Add a black border to accentuate white segments
path_effects=[
pe.Stroke(linewidth=lw + 1, foreground="black"),
pe.Normal(),
],
)

ValueError: Color array must be two-dimensional [duplicate]

I want to specify the frequency of markers that are printed in my scatter plot.
After being unsuccessful with markevery (other stackoverflow question: Problems with using markevery) I followed the suggestion to slice my values using the notation of x[::5] and y[::5] for every 5th value.
However, now I get a different error. That is,
Traceback (most recent call last):
File "C:\Users\mkupfer\NASA_SJSU_UARC_work\Info\CodingExamples\PythonExamples\X-Y-Value_Plot_Z-SimTime_02_noSectors.py", line 26, in <module>
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList, marker = marker.next(), edgecolors='none', norm=cNorm, cmap = plt.matplotlib.cm.jet) #cm.Spectral_r
File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 5715, in scatter
colors = mcolors.colorConverter.to_rgba_array(c, alpha)
File "C:\Python27\lib\site-packages\matplotlib\colors.py", line 380, in to_rgba_array
raise ValueError("Color array must be two-dimensional")
ValueError: Color array must be two-dimensional
Here is a simplified version of my code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.lines as lines
from matplotlib import cm
import csv
import itertools
import random
#callSignList = [AMF2052,AMF2052,AMF2052,AMF2052,AMF2052]
xList = random.sample(xrange(100), 100)
yList = random.sample(xrange(100), 100)
timeList = random.sample(xrange(100), 100)
#prepare the plot
fig = plt.figure(figsize=(18,13))
ax = fig.add_subplot(111)
cNorm = plt.matplotlib.colors.Normalize(vmin=0, vmax=3600)
marker = itertools.cycle(('o', '^', '+', '8', 's', 'p', 'x'))
x = xList
y = yList
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList, marker = marker.next(), edgecolors='none', norm=cNorm, cmap = plt.matplotlib.cm.jet) #cm.Spectral_r
fig.subplots_adjust(top=0.90, bottom=0.15, hspace=0.25,)
# Now adding the colorbar
#fig.colorbar(timePlot, shrink=0.5, aspect=10, orientation="horizontal")
cax = fig.add_axes([0.15, 0.06, 0.7, 0.05])
#The numbers in the square brackets of add_axes refer to [left, bottom, width, height],
#where the coordinates are just fractions that go from 0 to 1 of the plotting area.
#ax.colorbar(timePlot)
cbar = fig.colorbar(timePlot, cax, orientation='horizontal')
cbar.set_label('Relative Simulation Time')
plt.show()
Can someone please give me an idea where I made a mistake?
Any help is appreciated. Thanks.
Your colour list should be the same length as your data. So you need to apply the same slice.
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList[::5],
marker = marker.next(), edgecolors='none',
norm=cNorm, cmap = plt.matplotlib.cm.jet)

Value Error with color array when slicing values for scatter plot

I want to specify the frequency of markers that are printed in my scatter plot.
After being unsuccessful with markevery (other stackoverflow question: Problems with using markevery) I followed the suggestion to slice my values using the notation of x[::5] and y[::5] for every 5th value.
However, now I get a different error. That is,
Traceback (most recent call last):
File "C:\Users\mkupfer\NASA_SJSU_UARC_work\Info\CodingExamples\PythonExamples\X-Y-Value_Plot_Z-SimTime_02_noSectors.py", line 26, in <module>
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList, marker = marker.next(), edgecolors='none', norm=cNorm, cmap = plt.matplotlib.cm.jet) #cm.Spectral_r
File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 5715, in scatter
colors = mcolors.colorConverter.to_rgba_array(c, alpha)
File "C:\Python27\lib\site-packages\matplotlib\colors.py", line 380, in to_rgba_array
raise ValueError("Color array must be two-dimensional")
ValueError: Color array must be two-dimensional
Here is a simplified version of my code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.lines as lines
from matplotlib import cm
import csv
import itertools
import random
#callSignList = [AMF2052,AMF2052,AMF2052,AMF2052,AMF2052]
xList = random.sample(xrange(100), 100)
yList = random.sample(xrange(100), 100)
timeList = random.sample(xrange(100), 100)
#prepare the plot
fig = plt.figure(figsize=(18,13))
ax = fig.add_subplot(111)
cNorm = plt.matplotlib.colors.Normalize(vmin=0, vmax=3600)
marker = itertools.cycle(('o', '^', '+', '8', 's', 'p', 'x'))
x = xList
y = yList
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList, marker = marker.next(), edgecolors='none', norm=cNorm, cmap = plt.matplotlib.cm.jet) #cm.Spectral_r
fig.subplots_adjust(top=0.90, bottom=0.15, hspace=0.25,)
# Now adding the colorbar
#fig.colorbar(timePlot, shrink=0.5, aspect=10, orientation="horizontal")
cax = fig.add_axes([0.15, 0.06, 0.7, 0.05])
#The numbers in the square brackets of add_axes refer to [left, bottom, width, height],
#where the coordinates are just fractions that go from 0 to 1 of the plotting area.
#ax.colorbar(timePlot)
cbar = fig.colorbar(timePlot, cax, orientation='horizontal')
cbar.set_label('Relative Simulation Time')
plt.show()
Can someone please give me an idea where I made a mistake?
Any help is appreciated. Thanks.
Your colour list should be the same length as your data. So you need to apply the same slice.
timePlot = ax.scatter(x[::5], y[::5], s=50, c=timeList[::5],
marker = marker.next(), edgecolors='none',
norm=cNorm, cmap = plt.matplotlib.cm.jet)

Categories