Say below example code, the annotation line not match the start and end point exactly:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def annotate_delta(ax,df0,colname,col0,col1):
x = col1
y0 = df0[colname].iloc[col0]
y1 = df0[colname].iloc[col1]
d = 0.05
ax.annotate('',
xy=(x+d, y0),
xytext=(x+d, y1),
xycoords='data',
textcoords='data',
arrowprops=dict(arrowstyle='|-|',facecolor='red',color='r'),
annotation_clip=False)
ax.annotate('%.1f'%(y1-y0),
xy=(x+d+0.15, (y1+y0)/2),
color='r',ha='center',
va='center',
rotation=-90
,annotation_clip=False)
return
def plotme(df0,label):
fig = plt.figure(figsize=(12,6))
ax1 = fig.add_subplot(111)
xcol = 'A'
ycol = 'B'
df.plot(x=xcol,y=ycol,ax=ax1,marker='.')
annotate_delta(ax1,df0,ycol,0,len(df0)-1)
ax1.set_xticks(np.arange(len(df0)))
ax1.set_xticklabels(df0[xcol],rotation=45, ha='right')
plt.tight_layout()
ax1.grid(axis='y')
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
plt.xlabel("X")
plt.ylabel("Y")
fig.savefig("demo.png", dpi=fig.dpi)
return
df = pd.DataFrame({'A':['apple','orange','bananna','watermelon'],'B':[1,3.5,2.5,4]})
plotme(df,"Sample")
Arrow annotations are shrunk by 2 points by default:
shrinkA: default is 2 points
shrinkB: default is 2 points
Set shrinkA=0 and shrinkB=0 in arrowprops to remove the default padding:
arrowprops=dict(arrowstyle='|-|',color='r',shrinkA=0,shrinkB=0)
Related
I would like to adjust the bar value text position below each bar top with barwidth/5 offset.
text_y -= bar.get_width()/5 # <- not work
Full code:
#!/usr/bin/env python3
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import pandas as pd
import matplotlib.patheffects as PathEffects
import math
import numpy as np
def save_fig(fig,pngname):
fig.savefig(pngname, dpi=fig.dpi, bbox_inches="tight")
print("[[%s]]"%pngname)
return
def plot_bar(df):
xname = df.columns[0]
fig, ax = plt.subplots(figsize=(10, 5))
x = np.arange(len(df[xname]))
n = len(df.columns[1:])
bar_width = 0.95/n
fontsize = 20
colors = ['#5891ad','#004561','#ff6f31','#1c7685','#0f45a8','#4cdc8b','#0097a7']
dy = -bar_width/10
bars = []
# add bars
for i,colname in enumerate(df.columns[1:]):
bar = ax.bar(x+i*bar_width, df[colname], width=bar_width,color=colors[i])
bars.append(bar)
# add text on bars
for bar in ax.patches:
bar_value = bar.get_height()
text = f'{bar_value:,}'
text_x = bar.get_x() + bar.get_width() / 2
text_y = bar.get_y() + bar_value
text_y -= bar.get_width()/5 # <- not work
bar_color = bar.get_facecolor()
t = ax.text(text_x, text_y, text, ha='center', va='top', color=bar_color,
size=fontsize)
t.set_path_effects([PathEffects.withStroke(linewidth=bar_width*15, foreground='w')])
ax.set_xticks(x + 1/ 2 - bar_width/2)
ax.set_xticklabels(df[xname])
ax.legend()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
#ax.spines['left'].set_visible(False)
#ax.spines['bottom'].set_color('#DDDDDD')
ax.tick_params(bottom=False, left=False)
ax.set_axisbelow(True)
ax.yaxis.grid(True, color='#EEEEEE')
ax.xaxis.grid(False)
ax.set_xlabel('x', labelpad=15)
ax.set_ylabel('y', labelpad=15)
ax.set_title('title', pad=15)
fig.tight_layout()
plt.show()
return
data = [['a',3,2,1],
['b',2,3,1],
['c',3,1,3],
['d',5,1,3],
]
df = pd.DataFrame(data,columns=['f1','f2','f3','f4'])
plot_bar(df)
In order to create a 3d plot using plot_surface and wireframe I wrote this (looking here around)
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
data=np.genfromtxt('jpdfomegal2_90.dat')
x_len= len(np.unique(data[:, 0]))
y_len= len(np.unique(data[:, 1]))
X = data[:, 0].reshape(x_len, y_len)
Y = data[:, 1].reshape(x_len, y_len)
Z = data[:, 2].reshape(x_len, y_len)
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
#log transformation because the conversion in 3D
#does not work well in matplotlib
Zlog = np.log10(Z)
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
#ax = fig.add_subplot(projection='3d')
ax = Axes3D(fig)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(10)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
rc('font',family='palatino')
rc('font',size=18)
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
plt.tight_layout()
plt.savefig('jpdf_lambda2_90.png', bbox_inches='tight')
plt.show()
the problem is related to the "minorticks" along zaxis .. I obtain this :
but I would have this format and ticks in the axis
Does somebody clarify how to obtain it and as well I did not find a way to use the log scale in pyplot 3d
There's an open bug on log-scaling in 3D plots, and it looks like there won't be a fix any time soon.
You can use a matplotlib.ticker.FixedLocator to add the z-axis minor ticks, as shown below.
I didn't have your data, so I've plotted an arbitrary surface.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import MultipleLocator, FixedLocator
import matplotlib.ticker as mticker
import numpy as np
from matplotlib.ticker import FormatStrFormatter
def log_tick_formatter(val, pos=None):
return f"10$^{{{int(val)}}}$"
x = np.linspace(1,15,15)
y = np.linspace(0,1,15)
X, Y = np.meshgrid(x, y)
Z = 1 + X**2 * Y**2
#identify lowest non-negative Z value Zmin>0
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
#and substitute zero with a slightly lower value than Zmin
Z[Z==0] = 0.9 * Zmin
rc('font',family='palatino')
rc('font',size=18)
fig = plt.figure(figsize=(12,8))
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-1, np.ceil(np.log10(Zmax)))
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
tmp_planes = ax.zaxis._PLANES
ax.zaxis._PLANES = ( tmp_planes[2], tmp_planes[3],
tmp_planes[0], tmp_planes[1],
tmp_planes[4], tmp_planes[5])
ax.set_xlabel('$\omega^2 /<\omega^2>$')
ax.xaxis.labelpad = 10
ax.yaxis.labelpad = 10
ax.set_ylabel('cos$(\omega,\lambda^2)$')
ax.zaxis.set_rotate_label(False) # disable automatic rotation
ax.zaxis.labelpad = 10
ax.set_zlabel('')
ax.view_init(elev=17, azim=-60)
ax.grid(False)
ax.xaxis.pane.set_edgecolor('black')
ax.yaxis.pane.set_edgecolor('black')
ax.zaxis.pane.set_edgecolor('black')
ax.xaxis.pane.fill = False
ax.yaxis.pane.fill = False
ax.zaxis.pane.fill = False
ax.xaxis.set_major_locator(MultipleLocator(2))
ax.yaxis.set_major_locator(MultipleLocator(0.2))
ax.zaxis.set_major_locator(MultipleLocator(1))
# Z minor ticks
zminorticks = []
zaxmin, zaxmax = ax.get_zlim()
for zorder in np.arange(np.floor(zaxmin),
np.ceil(zaxmax)):
zminorticks.extend(np.log10(np.linspace(2,9,8)) + zorder)
ax.zaxis.set_minor_locator(FixedLocator(zminorticks))
#not sure this axis scaling routine is really necessary
scale_x = 1
scale_y = 1
scale_z = 0.8
ax.get_proj = lambda: np.dot(Axes3D.get_proj(ax), np.diag([scale_x, scale_y, scale_z, 1]))
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-1)#-7)
surf = ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=1,color='k')
ax.contour(X, Y, np.log10(Z), 4, colors="k", linestyles="solid")
fig.colorbar(surf, shrink=0.5, aspect=20)
# get a warning that Axes3D is incompatible with tight_layout()
# plt.tight_layout()
# for saving
# fig.savefig('log3d.png')
plt.show()
I want to connect airplanes in origin (lat_1 lon_1) to dest(lat_2 lon_2). I use these data.
callsign
latitude_1
longitude_1
latitude_2
longitude_2
0
HBAL102
-4.82114
-76.3194
-4.5249
-79.0103
1
AUA1028
-33.9635
151.181
48.1174
16.55
2
ABW120
41.9659
-87.8832
55.9835
37.4958
3
CSN461
33.9363
-118.414
50.0357
8.5723
4
ETH3730
25.3864
55.4221
50.6342
5.43903
But unfortunately, I would get an incorrect result when creating LineString with shapely. I used everything like rotate and affine but it didn't correct.
Code:
cols = pd.read_csv("/content/dirct_lines.csv",sep=";")
line = cols[["callsign","latitude_1","longitude_1","latitude_2","longitude_2"]].dropna()
line['geometry'] = line.apply(lambda x: [(x['latitude_1'],
x['longitude_1']),
(x['latitude_2'],
x['longitude_2'])], axis = 1)
geoline = gpd.GeoDataFrame(line,geometry="geometry",
crs="EPSG:4326")
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path('naturalearth_lowres'))
ax = world.plot(figsize=(14,9),
color='white', edgecolor='black')
geoline.plot(figsize=(14,9),ax=ax,facecolor = 'lightgrey', linewidth = 1.75,
edgecolor = 'red',
alpha = 2)
plt.show()
Shapely Output:
something that was interesting for me was that when I use Matplotlib to create lines everything is correct.
Code:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(projection=ccrs.PlateCarree())
ax.stock_img()
org_lon, org_lat = cols["longitude_1"], cols["latitude_1"]
dst_lon, dst_lat = cols["longitude_2"], cols["latitude_2"]
plt.plot([org_lon, dst_lon], [org_lat, dst_lat],
color='black', linewidth=0.5, marker='_',
transform=ccrs.PlateCarree()
)
plt.savefig(f"fight_path.png",dpi=60,facecolor = None, bbox_inches = 'tight', pad_inches = None)
plt.show()
Matplotlib Output:
What is the problem?
why isn't correct by shapely?
it's just the way you are creating the geometry. Below works correctly.
import io
import geopandas as gpd
import pandas as pd
import shapely.geometry
df = pd.read_csv(
io.StringIO(
"""callsign,latitude_1,longitude_1,latitude_2,longitude_2
HBAL102,-4.82114,-76.3194,-4.5249,-79.0103
AUA1028,-33.9635,151.181,48.1174,16.55
ABW120,41.9659,-87.8832,55.9835,37.4958
CSN461,33.9363,-118.414,50.0357,8.5723
ETH3730,25.3864,55.4221,50.6342,5.43903
"""
)
)
geoline = gpd.GeoDataFrame(
geometry=[
shapely.geometry.LineString(points)
for points in zip(
gpd.points_from_xy(df["longitude_1"], df["latitude_1"]),
gpd.points_from_xy(df["longitude_2"], df["latitude_2"]),
)
],
data=df,
)
import matplotlib.pyplot as plt
world = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
ax = world.plot(figsize=(14, 9), color="white", edgecolor="black")
geoline.plot(
figsize=(14, 9),
ax=ax,
facecolor="lightgrey",
linewidth=1.75,
edgecolor="red",
)
plt.show()
I read the document of matplotlib and write the following code, it supposed to capture my mouse event and move the grey line position when i clicked. I read this code in jupiter notebook online, it stop to show the coordinate of my cursor as it usually do, What's happend? Can anyone help me?
import pandas as pd
import numpy as np
import matplotlib.colors as mcol
import matplotlib.cm as cm
from scipy import stats
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import scipy.spatial as spatial
np.random.seed(12345)
df = pd.DataFrame([np.random.normal(33500,150000,3650),
np.random.normal(41000,90000,3650),
np.random.normal(41000,120000,3650),
np.random.normal(48000,55000,3650)],
index=[1992,1993,1994,1995])
fig, ax = plt.subplots()
year_avg = df.mean(axis = 1)
year_std = df.std(axis = 1)
yerr = year_std / np.sqrt(df.shape[1]) * stats.t.ppf(1-0.05/2, df.shape[1]-1)
bars=ax.bar(range(df.shape[0]), year_avg, yerr = yerr, color = 'lightslategrey')
threshold=42000
line=plt.axhline(y = threshold, color = 'grey', alpha = 0.5)
cm1 = mcol.LinearSegmentedColormap.from_list("CmapName",["yellow", "orange", "red"])
cpick = cm.ScalarMappable(cmap=cm1)
percentages = []
cpick.set_array([])
def setColor(bars, yerr,threshold):
for bar, yerr_ in zip(bars, yerr):
low = bar.get_height() - yerr_
high = bar.get_height() + yerr_
percentage = (high-threshold)/(high-low)
if percentage>1: percentage = 1
if percentage<0: percentage=0
percentages.append(percentage)
cpick.to_rgba(percentages)
bars = ax.bar(range(df.shape[0]), year_avg, yerr = yerr, color = cpick.to_rgba(percentages))
return bars
line=plt.axhline(threshold, color = 'grey', alpha = 0.5)
setColor(bars, yerr,threshold)
plt.colorbar(cpick, orientation='horizontal')
plt.xticks(range(df.shape[0]), df.index)
fig = plt.figure()
plt.show()
def onclick(event):
print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' %
('double' if event.dblclick else 'single', event.button,
event.x, event.y, event.xdata, event.ydata))
line.set_ydata(event.ydata)
#plt.draw()
cid = fig.canvas.mpl_connect('button_press_event', onclick)
I use matplotlib.pyplot.pcolor() to plot a heatmap with matplotlib:
import numpy as np
import matplotlib.pyplot as plt
def heatmap(data, title, xlabel, ylabel):
plt.figure()
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap='RdBu', vmin=0.0, vmax=1.0)
plt.colorbar(c)
def main():
title = "ROC's AUC"
xlabel= "Timeshift"
ylabel="Scales"
data = np.random.rand(8,12)
heatmap(data, title, xlabel, ylabel)
plt.show()
if __name__ == "__main__":
main()
Is any way to add the corresponding value in each cell, e.g.:
(from Matlab's Customizable Heat Maps)
(I don't need the additional % for my current application, though I'd be curious to know for the future)
You need to add all the text by calling axes.text(), here is an example:
import numpy as np
import matplotlib.pyplot as plt
title = "ROC's AUC"
xlabel= "Timeshift"
ylabel="Scales"
data = np.random.rand(8,12)
plt.figure(figsize=(12, 6))
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap='RdBu', vmin=0.0, vmax=1.0)
def show_values(pc, fmt="%.2f", **kw):
from itertools import izip
pc.update_scalarmappable()
ax = pc.get_axes()
for p, color, value in izip(pc.get_paths(), pc.get_facecolors(), pc.get_array()):
x, y = p.vertices[:-2, :].mean(0)
if np.all(color[:3] > 0.5):
color = (0.0, 0.0, 0.0)
else:
color = (1.0, 1.0, 1.0)
ax.text(x, y, fmt % value, ha="center", va="center", color=color, **kw)
show_values(c)
plt.colorbar(c)
the output:
You could use Seaborn, which is a Python visualization library based on matplotlib that provides a high-level interface for drawing attractive statistical graphics.
Heatmap example:
import seaborn as sns
sns.set()
flights_long = sns.load_dataset("flights")
flights = flights_long.pivot("month", "year", "passengers")
sns.heatmap(flights, annot=True, fmt="d")
# To display the heatmap
import matplotlib.pyplot as plt
plt.show()
# To save the heatmap as a file:
fig = heatmap.get_figure()
fig.savefig('heatmap.pdf')
Documentation: https://seaborn.pydata.org/generated/seaborn.heatmap.html
If that's of interest to anyone, here is below the code I use to imitate the picture from Matlab's Customizable Heat Maps I had included in the question).
import numpy as np
import matplotlib.pyplot as plt
def show_values(pc, fmt="%.2f", **kw):
'''
Heatmap with text in each cell with matplotlib's pyplot
Source: http://stackoverflow.com/a/25074150/395857
By HYRY
'''
from itertools import izip
pc.update_scalarmappable()
ax = pc.get_axes()
for p, color, value in izip(pc.get_paths(), pc.get_facecolors(), pc.get_array()):
x, y = p.vertices[:-2, :].mean(0)
if np.all(color[:3] > 0.5):
color = (0.0, 0.0, 0.0)
else:
color = (1.0, 1.0, 1.0)
ax.text(x, y, fmt % value, ha="center", va="center", color=color, **kw)
def cm2inch(*tupl):
'''
Specify figure size in centimeter in matplotlib
Source: http://stackoverflow.com/a/22787457/395857
By gns-ank
'''
inch = 2.54
if type(tupl[0]) == tuple:
return tuple(i/inch for i in tupl[0])
else:
return tuple(i/inch for i in tupl)
def heatmap(AUC, title, xlabel, ylabel, xticklabels, yticklabels):
'''
Inspired by:
- http://stackoverflow.com/a/16124677/395857
- http://stackoverflow.com/a/25074150/395857
'''
# Plot it out
fig, ax = plt.subplots()
c = ax.pcolor(AUC, edgecolors='k', linestyle= 'dashed', linewidths=0.2, cmap='RdBu', vmin=0.0, vmax=1.0)
# put the major ticks at the middle of each cell
ax.set_yticks(np.arange(AUC.shape[0]) + 0.5, minor=False)
ax.set_xticks(np.arange(AUC.shape[1]) + 0.5, minor=False)
# set tick labels
#ax.set_xticklabels(np.arange(1,AUC.shape[1]+1), minor=False)
ax.set_xticklabels(xticklabels, minor=False)
ax.set_yticklabels(yticklabels, minor=False)
# set title and x/y labels
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
# Remove last blank column
plt.xlim( (0, AUC.shape[1]) )
# Turn off all the ticks
ax = plt.gca()
for t in ax.xaxis.get_major_ticks():
t.tick1On = False
t.tick2On = False
for t in ax.yaxis.get_major_ticks():
t.tick1On = False
t.tick2On = False
# Add color bar
plt.colorbar(c)
# Add text in each cell
show_values(c)
# resize
fig = plt.gcf()
fig.set_size_inches(cm2inch(40, 20))
def main():
x_axis_size = 19
y_axis_size = 10
title = "ROC's AUC"
xlabel= "Timeshift"
ylabel="Scales"
data = np.random.rand(y_axis_size,x_axis_size)
xticklabels = range(1, x_axis_size+1) # could be text
yticklabels = range(1, y_axis_size+1) # could be text
heatmap(data, title, xlabel, ylabel, xticklabels, yticklabels)
plt.savefig('image_output.png', dpi=300, format='png', bbox_inches='tight') # use format='svg' or 'pdf' for vectorial pictures
plt.show()
if __name__ == "__main__":
main()
#cProfile.run('main()') # if you want to do some profiling
Output:
It looks nicer when there are some patterns:
Same as #HYRY aswer, but python3 compatible version:
import numpy as np
import matplotlib.pyplot as plt
title = "ROC's AUC"
xlabel= "Timeshift"
ylabel="Scales"
data = np.random.rand(8,12)
plt.figure(figsize=(12, 6))
plt.title(title)
plt.xlabel(xlabel)
plt.ylabel(ylabel)
c = plt.pcolor(data, edgecolors='k', linewidths=4, cmap='RdBu', vmin=0.0, vmax=1.0)
def show_values(pc, fmt="%.2f", **kw):
pc.update_scalarmappable()
ax = pc.axes
for p, color, value in zip(pc.get_paths(), pc.get_facecolors(), pc.get_array()):
x, y = p.vertices[:-2, :].mean(0)
if np.all(color[:3] > 0.5):
color = (0.0, 0.0, 0.0)
else:
color = (1.0, 1.0, 1.0)
ax.text(x, y, fmt % value, ha="center", va="center", color=color, **kw)
show_values(c)
plt.colorbar(c)