Related
So I have some data in spherical coords, but r is not important. So I really have (theta,phi,value), where theta goes 0-360 deg and phi 0-90 deg... Values go from -40 to 40 ... I can plot this data using pcolormesh on a polar diagram,
phis2 = np.linspace(0.001,63,201)
thetas2 = np.linspace(0,2*np.pi,201)
# Using same number of samples in phi and thera to simplify plotting
print(phis2.shape,thetas2.shape)
X,Y = np.meshgrid(thetas2,phis2)
doppMap2 =orbits.doppler(X*units.rad,Y*deg) # Calling function with a vector: MUCH faster than looping as above
fig, ax = plt.subplots(figsize=(8,7),subplot_kw=dict(projection='polar'))
im=ax.pcolormesh(X,Y,doppMap2,cmap=mpl.cm.jet_r, edgecolors='face')
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.set_xticks([x for x in np.linspace(0,2*np.pi,13)][:-1]) # ignore label 360
ax.grid(True)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.text(.6, 1.025, "Nadir ang", transform=ax.transAxes, fontsize=14)
## Add colorbar
cbar_ax = fig.add_axes([0.95, 0.15, 0.015, 0.7])
cbar = fig.colorbar(im, cax=cbar_ax)
cbar.ax.tick_params(labelsize=14)
#cbar.ax.set_yticklabels(['1', '2', '4', '6', '10', maxCV], size=24)
#cbar.set_label(r"log ($P(\overline{Z_{G}} /Z_{\odot})$ / $d(M_{G}/M_{\odot})$)",fontsize=36)
cbar.set_label(r"$d$f [kHz]",fontsize=24)
gc.collect()
but I'd like to generate isochrone lines instead. How would I do that?
Data for doppMap2 is here...
Matplotlib calls that a contour map:
# answering https://stackoverflow.com/questions/74073323/isochrone-plot-in-polar-coordinates
import numpy as np
import pandas
import matplotlib as mpl
import matplotlib.pyplot as plt
phis2 = np.linspace(0.001,63,201)
thetas2 = np.linspace(0,2*np.pi,201)
# Using same number of samples in phi and thera to simplify plotting
print(phis2.shape,thetas2.shape)
X,Y = np.meshgrid(thetas2,phis2)
# doppMap2 = orbits.doppler(X*units.rad,Y*deg) # Calling function with a vector: MUCH faster than looping as above
doppMap2 = pandas.read_csv('dopMap.csv', header=None)
print(doppMap2.shape)
fig, ax = plt.subplots(figsize=(8,7),subplot_kw=dict(projection='polar'))
im = ax.contour(X, Y, doppMap2, 12)
ax.set_theta_direction(-1)
ax.set_theta_offset(np.pi / 2.0)
ax.set_xticks([x for x in np.linspace(0,2*np.pi,13)][:-1]) # ignore label 360
ax.grid(True)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.text(.6, 1.025, "Nadir ang",
transform=ax.transAxes, fontsize=14)
## Add colorbar
cbar_ax = fig.add_axes([0.95, 0.15, 0.015, 0.7])
cbar = fig.colorbar(im, cax=cbar_ax)
cbar.ax.tick_params(labelsize=14)
cbar.set_label(r"$d$f [kHz]",fontsize=24)
plt.show()
[UPDATE: Sorry for not providing the piece where the author of the codes create example data. I have updated the codes]
I found an example of a 3D mesh line chart that satisfied what I need (colouring change with level on z dimension). However, instead of line, I want surface plot. How can I change the codes to have the 3d surface plot?
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import animation, rc
from matplotlib.cm import get_cmap
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.font_manager import FontProperties
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap
from mpl_toolkits.mplot3d.art3d import Line3DCollection
index_returns = np.random.normal(loc=1e-4, scale=5e-3, size=(783, 9))
index_returns = np.vstack((np.zeros(shape=(1, 9)) + 100, index_returns))
index_prices = np.cumprod(1 + index_returns, axis=0)
window = 261
df = np.zeros(shape=(index_prices.shape[0]-window, 9))
for i in range(window, index_prices.shape[0], 1):
df[i-window] = (index_prices[i]/index_prices[i-window]) - 1
index = pd.date_range('2019-01-01', periods=index_prices.shape[0]-window, freq='B')
columns = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
df = pd.DataFrame(df, index=index, columns=columns)
# create the figure
fig = plt.figure(figsize=(14.4, 9))
ax = fig.add_subplot(111, projection='3d')
fig.patch.set_alpha(1)
# get the cmap to use
cmap = get_cmap('RdYlGn')
# get the slice based on data frame
current_slice = df.values[:261, :]
index_names = df.columns
index_dates = df.index
# list holding the lines
lines = []
# for each index...
for i in range(current_slice.shape[1]):
# get the coordinates
x = np.array(np.arange(current_slice.shape[0]))
y = np.tile(i, current_slice.shape[0])
z = np.array(current_slice[:, i])
# crete points and segments to color
points = np.array([x, y, z]).T.reshape(-1, 1, 3)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# Create a continuous norm to map from data points to colors
norm = plt.Normalize(-0.19, 0.19)
lc = Line3DCollection(segments, cmap=cmap, norm=norm, zorder=current_slice.shape[1]-i)
# Set the values used for colormapping
lc.set_array(z)
lc.set_linewidth(2)
lc.set_color(cmap(z[-1] * 2.5 + 0.5))
lc.set_label(index_names[i])
lines.append(ax.add_collection(lc))
# add the grids
ax.legend(loc='center right', bbox_to_anchor=(1.1, 0.46), fancybox=True, facecolor=(.95,.95,.95,1), framealpha=1, shadow=False, frameon=True, ncol=1, columnspacing=0, prop={'family': 'DejaVu Sans Mono'})
ax.set_zlabel('Rolling Equity 1Y', labelpad=10)
ax.set_zlim(-0.39, 0.39)
ax.set_zticklabels([' '* 3 + '{:.0%}'.format(val) for val in ax.get_zticks()], fontdict={'verticalalignment': 'center', 'horizontalalignment': 'center'})
ax.set_xlabel('Date', labelpad=30)
ax.set_xlim(0, current_slice.shape[0]-1)
ax.set_xticklabels([index_dates[int(val)].strftime('%m/%y') for val in ax.get_xticks()[:-1]] + [''], rotation=0, fontdict={'verticalalignment': 'top', 'horizontalalignment': 'center'})
ax.set_yticks(np.arange(current_slice.shape[1]))
ax.set_yticklabels([index_names[i] for i in range(current_slice.shape[1])], rotation=-15, fontdict={'verticalalignment': 'center', 'horizontalalignment': 'left'})
# show the plot
plt.show()
I am making my first 3D graph in Python on an Anaconda Jupyter Notebook. The idea is to obtain a graph with a format similar to the following:
The code I made is as follows:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
fig = plt.figure()
ax = Axes3D(fig)
def func(x, pos): # formatter function takes tick label and tick position
s = str(x)
ind = s.index('.')
return s[:ind] + ',' + s[ind+1:] # change dot to comma
x_format = tkr.FuncFormatter(func)
ax.xaxis.set_major_formatter(x_format)
ax.yaxis.set_major_formatter(x_format)
df = pd.read_excel('EDS 7.xlsx', header=None, usecols=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15], names=['A', 'B', 'C', 'D','E','F','G','H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'])
plt.rcParams["figure.figsize"] = [14.5,10]
nomes = ['Triângulo de MoSe$_2$','Losango branco','Losango cinzento','Fundo']
ax.set_yticks(range(0,4))
ax.set_yticklabels(nomes)
ax.tick_params(axis='z', pad=10)
ax.tick_params(axis='y', pad=20)
# put 0s on the y-axis, and put the y axis on the z-axis
ax.plot(xs=df['A'], ys=df['B'], zs=df['C'], zdir='z', label='ys=0, zdir=z', color='blue', linewidth=3)
ax.plot(xs=df['D'], ys=df['E'], zs=df['F'], zdir='z', label='ys=0, zdir=z', color='red', linewidth=3)
ax.plot(xs=df['G'], ys=df['H'], zs=df['I'], zdir='z', label='ys=0, zdir=z', color='green', linewidth=3)
ax.plot(xs=df['J'], ys=df['K'], zs=df['L'], zdir='z', label='ys=0, zdir=z', color='orange', linewidth=3)
y=df['M'];
plt.xlim([0.0, 4.0])
#plt.ylim([0.0, 4.0])
ax.set_zlim(0,1400)
plt.rc('xtick', labelsize=16)
plt.rc('ytick', labelsize=16)
#plt.xticks(np.arange(0.0,1.4,0.1).round(decimals=1))
#plt.yticks(np.arange(-0.8,1.3,0.2).round(decimals=1))
ax.w_xaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
ax.w_yaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
ax.w_zaxis.set_pane_color((1.0, 1.0, 1.0, 1.0))
ax.xaxis._axinfo["grid"]['linestyle'] = '--'
ax.xaxis._axinfo["grid"]['color'] = 'silver'
ax.yaxis._axinfo["grid"]['linestyle'] = '--'
ax.yaxis._axinfo["grid"]['color'] = 'silver'
ax.zaxis._axinfo["grid"]['linestyle'] = '--'
ax.zaxis._axinfo["grid"]['color'] = 'silver'
ax.set_xlabel('Energia (keV)', fontsize=20, labelpad=18)
ax.set_zlabel('Contagens', fontsize=20, labelpad=18)
#plt.show()
plt.savefig('output.png', dpi=500, bbox_inches='tight')
Excel file:
The graphic I got is this:
I am having two problems that I am unable to solve:
The underside of the lines is not filled with color and I would like them to be opaque.
In the yy axis, the strings are too far to the left and for example the string "Triângulo de MoSe2" of the yy axis is to the left of the number 4.0 of the xx axis. I would like the y-axis strings to be more centered.
How can I adjust the code for the graph to have these two characteristics that I lack?
Here is an example to create something similar to the desired plot. Some toy data are used to create 4 curves.
To fill the area below the curves, the approach from this tutorial is used. For the y tick labels, it seems ax.set_yticklabels(..., ha='left') together with ax.tick_params(axis='y', pad=0) get quite close to the desired result.
To make the polygons fully opaque, set the opaqueness alpha in PolyCollection(...) to a value closer to 1. Usually a small bit of transparency gives a better feeling of being a 3D plot. You can leave out the call to ax.plot(...) if the thicker "border" isn't needed.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.collections import PolyCollection
def polygon_under_graph(xlist, ylist):
return [(xlist[0], 0.), *zip(xlist, ylist), (xlist[-1], 0.)]
x_cols = {col: np.linspace(0, 4, 50) for col in [*'ADGJ']}
z_cols = {col: 1400 ** np.random.rand(50) for col in [*'CFIL']}
df = pd.DataFrame({**x_cols, **z_cols})
fig = plt.figure()
ax = Axes3D(fig)
plt.rcParams["figure.figsize"] = [14.5, 10]
nomes = ['Triângulo de MoSe$_2$', 'Losango branco', 'Losango cinzento', 'Fundo']
ax.set_yticks(range(0, 4))
ax.set_yticklabels(nomes, ha='left')
ax.tick_params(axis='z', pad=10)
ax.tick_params(axis='y', pad=0)
color_list = ['cornflowerblue', 'crimson', 'limegreen', 'orange']
verts = []
ys = [3, 2, 1, 0]
for x_col, z_col, y, color in zip(['A', 'D', 'G', 'J'], ['C', 'F', 'I', 'L'], ys, color_list):
xs = df[x_col].to_numpy()
zs = df[z_col].to_numpy()
ax.plot(xs=xs, ys=np.repeat(y, len(xs)), zs=zs, zdir='z', color=color, linewidth=3)
verts.append(polygon_under_graph(xs, zs))
poly = PolyCollection(verts, facecolors=color_list, alpha=.8)
ax.add_collection3d(poly, zs=ys, zdir='y')
plt.show()
About having the outlines of the 3 panes in black, some experimenting with 3D figures from Matplotlib visibility of pane edge leads to the following. It is unclear to me why that hack works (an other approaches don't).
def lims(mplotlims):
scale = 1.021
offset = (mplotlims[1] - mplotlims[0]) * scale
return mplotlims[1] - offset, mplotlims[0] + offset
xlims, ylims, zlims = lims(ax.get_xlim()), lims(ax.get_ylim()), lims(ax.get_zlim())
i = np.array([xlims[0], ylims[0], zlims[0]])
f = np.array([xlims[0], ylims[0], zlims[1]])
p = art3d.Poly3DCollection(np.array([[i, f]]))
p.set_color('black')
ax.add_collection3d(p)
ax.xaxis.pane.set_edgecolor('#000000FF')
ax.yaxis.pane.set_edgecolor('#000000FF')
ax.zaxis.pane.set_edgecolor('#000000FF')
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°_urb=DEG1°_urb=DEG2°_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()
I'm plotting some function in matplotlib. But I want to change the usual x and y coordinates. For example I plot y=sin(x) in [-pi, pi]. But the x-axis shows 1, 2, 3,... in this way whereas I want x: -pi, 0, pi,... Is it possible?
My Code
import matplotlib as mpl
mpl.rc('text', usetex = True)
mpl.rc('font', family = 'serif')
import matplotlib.pyplot as plt
import numpy as np
plt.gca().set_aspect('equal', adjustable='box')
plt.style.use(['ggplot','dark_background'])
x = np.arange(-np.pi,np.pi,0.001)
y = np.sin(x)
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.plot(x,y, label='$y=\sin x$')
plt.legend()
plt.show()
Output
How to change the marks on the axes coordinates? Thank you.
Yes, you can have custom tick marks on the axis, and set them equally spaced; for this you need to set the tick marks as a sequence, together with the values associated:
import matplotlib as mpl
mpl.rc('text', usetex = True)
mpl.rc('font', family = 'serif')
import matplotlib.pyplot as plt
import numpy as np
plt.gca().set_aspect('equal', adjustable='box')
plt.style.use(['ggplot','dark_background'])
x = np.arange(-np.pi,np.pi,0.001)
y = np.sin(x)
# the following two sequences contain the values and their assigned tick markers
xx = [-np.pi + idx*np.pi/4 for idx in range(10)]
xx_t = ['$-\\pi$', '$\\frac{-3\\pi}{4}$', '$\\frac{-\\pi}{2}$', '$\\frac{-\\pi}{4}$', '0',
'$\\frac{\\pi}{4}$', '$\\frac{\\pi}{2}$', '$\\frac{3\\pi}{4}$', '$\\pi$']
plt.xticks(xx, xx_t) # <-- the mapping happens here
plt.xlabel('$x$')
plt.ylabel('$y$')
plt.plot(x,y, label='$y=\sin x$')
plt.legend()
plt.show()
Here you can display up to whichever range of pi you want to. Just add the following lines to your code after plt.plot
xlabs = [r'%d$\pi$'%i if i!=0 else 0 for i in range(-2, 3, 1)]
xpos = np.linspace(-2*np.pi, 2*np.pi, 5)
plt.xticks(xpos, xlabs)
Output