matplotlib twinx xticks at specific locations - python

I have the code below, in the x-axis, i want to show only the parameter values for which i have the metric values which are 5,10,20 and 50.
I want the parameter values to span the x-axis.
How I can do it ?.
import matplotlib.pyplot as plt;
import numpy as np;
from matplotlib import rc;
fig, ax1 = plt.subplots();
rc('mathtext', default='regular');
x = np.array([5,10,20,50]);
cg1 = np.array([0.1,0.3,0.5,0.8]);
cg2 = np.array([0.2,0.2,0.4,0.7]);
cg3 = np.array([0.3,0.4,0.6,0.6]);
lns1 = ax1.plot(x,cg1,'b*:',label='1 CG');
lns2 = ax1.plot(x,cg2,'bo--',label='2 CG');
lns3 = ax1.plot(x,cg3,'bs-',label='3 CG');
ax1.set_ylabel('CG',color='b');
ax1.set_ylim([0,1]);
ax1.set_xlim([4,55]);
ax1.set_xticklabels([5,10,20,50]);
ax1.set_xlabel('K');
ax2 = ax1.twinx();
ld1 = np.array([0.8,0.5,0.2,0.2]);
ld2 = np.array([0.6,0.2,0.3,0.2]);
ld3 = np.array([0.2,0.4,0.6,0.2]);
lns4 = ax2.plot(x,ld1,'k*:',label='1 ld');
lns5 = ax2.plot(x,ld2,'ko--',label='2 ld');
lns6 = ax2.plot(x,ld3,'ks-',label='3 ld');
lns = lns1 + lns2 + lns3 + lns4 + lns5 + lns6;
labs = [l.get_label() for l in lns];
ax1.legend(lns, labs, loc='best', ncol=2);
ax2.set_ylabel('LD',color='k');
ax2.set_ylim([0,1]);
ax2.set_xlim([4,55]);
plt.show();

Try replacing line with ax1.set_xlim([4,55]) with this line:
ax1.set_xticks(x)
You may also want to remove ax2.set_xlim(...).
Does it give you what you expected?
UPDATE Following comments:
Please use these lines (NOTE: the order matters!):
ax1.set_xlim([4,55]);
ax1.set_xticks(x)
...
ax2.set_xlim([4,55]);
ax2.set_xticks(x)
And remove anything else that touches xticks, like any of these:
ax1.set_xticklabels([5,10,20,50]);
ax2.set_xticklabels([5,10,20,50]);
This should produce a chart like this:
Which has limits at [4, 55] and only selected tick values visible.

Related

Change "Q" quarterly data to custom "kv" in Matplotlib timeline chart on x axis Python

I have done the following timeline chart in Python. Where the data is in quarterly format by datetimeindex. However, I need to translate the graph into my local language and therefore replace "Q1", "Q2", "Q3", "Q4" with "kv1", "kv2", "kv3", "kv4". Is this possible? So I need the x axsis to be kv3, kv4, kv1 2022, kv2 instead of Q3, Q4, Q1 2022, Q2 and so fourth.
import random
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.pyplot import figure
import pandas
plt.style.use('seaborn-whitegrid')
matplotlib.rcParams['font.sans-serif'] = "Arial"
matplotlib.rcParams['font.family'] = "Arial"
categories = ['Car','Train','Boat', 'Plane', 'Walk' ]
cat_dict = dict(zip(categories, range(1, len(categories)+1)))
val_dict = dict(zip(range(1, len(categories)+1), categories))
dates = pandas.DatetimeIndex(freq='Q', start='2021-09-30', end='2023-12-31')
values = [random.choice(categories) for _ in range(len(dates))]
df = pandas.DataFrame(data=values, index=dates, columns=['category'])
df['plotval'] = [float('NaN'),1,1,3,1,float('NaN'),5,2,1,float('NaN')]
df['plotval'][0] = np.nan
plt.rcParams["figure.figsize"] = 4,3.5
plt.figure(dpi=1000)
fig, ax = plt.subplots()
df['plotval'].plot(ax=ax, style='^',color='darkblue', label = "Renteheving", markersize=12)
ax.margins(0.2)
ax.spines['top'].set_visible(False)
ax.yaxis.set_major_formatter(plt.FuncFormatter(lambda x, pos: val_dict.get(x)))
plt.yticks( weight = 'bold')
I tried to add
plt.xlabel(["kv1", "kv2", "kv3", "kv4"])
Which gave me
Help is as always highly appreciated.
Try to add this to your code:
# Call draw to populate tick labels
plt.draw()
# Change major labels
new_major_labels = []
for label in ax.get_xticklabels(minor=False):
s = label.get_text()
label.set_text(s.replace('Q', 'kv'))
new_major_labels.append(label)
ax.set_xticklabels(new_major_labels, minor=False)
# Change minor labels
new_minor_labels = []
for label in ax.get_xticklabels(minor=True):
s = label.get_text()
label.set_text(s.replace('Q', 'kv'))
new_minor_labels.append(label)
ax.set_xticklabels(new_minor_labels, minor=True)
It throws a warning which I don't understand, but I think it does what you want.
I could not test it because I can't reproduce your graph, but this should work:
D = {'Q1':'kv1', 'Q2':'kv2', 'Q3':'kv3', 'Q4':'kv4'} # define a dictionnary to replace labels
labels = [i.get_text() for i in ax.get_xticklabels()] # get former labels
labels = [i if i not in D.keys() else D[i] for i in labels] # replace it if in dictionnary
ax.set_xticklabels(labels) # apply the new labels

How would I add text to this matplotlib graph?

I have the following code and graph and am wondering how I could add some text to it. On each bar I would like the min (where it begins) and the max (where it ends) displayed just inside the boxes and I would also like to place the average of each data set where it falls inside the box. The code is what I already have and the link goes to an example of what the final product should look similar too.
import matplotlib.pyplot as plt
import matplotlib.patches as patches
preRawData = [60,55,67,68,70,71]
postRawData = [71,75,80,77,73,74]
dataDict = {}
delta = 0.4
minPre = min(preRawData)
minPost = min(postRawData)
maxPre = max(preRawData)
maxPost = max(postRawData)
preAverage = sum(preRawData)/len(preRawData)
postAverage = sum(postRawData)/len(postRawData)
preRange = (minPre, maxPre)
postRange = (minPost, maxPost)
dataDict.update({'Pre' : preRange})
dataDict.update({'Post' : postRange})
yspan = len(dataDict)
yplaces = [.5+i for i in range(yspan)]
ylabels = sorted(dataDict.keys())
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_yticks(yplaces)
ax.set_yticklabels(ylabels)
ax.set_ylim((0,yspan))
preStart, preEnd = dataDict['Pre']
postStart, postEnd = dataDict['Post']
low, hi = dataDict[ylabels[0]]
ax.add_patch(patches.Rectangle((postStart,yplaces[0]-
delta/2.0),postEnd-postStart,delta, edgecolor='black',
facecolor='red'))
if postStart<low : low=postStart
if postEnd>hi : hi=postEnd
ax.add_patch(patches.Rectangle((preStart,yplaces[1]-
delta/2.0),preEnd-preStart,delta, edgecolor='black',
facecolor='green'))
if preStart<low : low=preStart
if preEnd>hi : hi=preEnd
ax.plot((low,hi),(0,0))
xmin, xmax = ax.get_xlim()
ax.hlines(range(1, yspan),xmin,xmax)
ax.grid(axis='x')
plt.show()
https://imgur.com/a/uBSVydh

ternary plots as subplot

I want to draw multiple ternary graphs and thought to do this using matplotlib's subplot.
I'm just getting empty 'regular' plots though, not the ternary graphs I want in there. I found the usage of
figure, ax = plt.subplots()
tax = ternary.TernaryAxesSubplot(ax=ax)
so this seems to be possible, but can't really find out how to get this working. Any ideas?
Code I'm using:
I'm using a for loop as the data has columns named tria1-a, tria2-a, etc for the different triads
import ternary
import matplotlib.pyplot as plt
import pandas as pd
#configure file to import.
filename = 'somecsv.csv'
filelocation = 'location'
dfTriad = pd.read_csv(filelocation+filename)
# plot the data
scale = 33
figure, ax = plt.subplots()
tax = ternary.TernaryAxesSubplot(ax=ax, scale=scale)
figure.set_size_inches(10, 10)
tax.set_title("Scatter Plot", fontsize=20)
tax.boundary(linewidth=2.0)
tax.gridlines(multiple=1, color="blue")
tax.legend()
tax.ticks(axis='lbr', linewidth=1, multiple=5)
tax.clear_matplotlib_ticks()
#extract the xyz columns for the triads from the full dataset
for i in range(1,6) :
key_x = 'tria'+ str(i) + '-a'
key_y = 'tria' + str(i) + '-b'
key_z = 'tria' + str(i) + '-c'
#construct dataframe from the extracted xyz columns
dfTriad_data = pd.DataFrame(dfTriad[key_x], columns=['X'])
dfTriad_data['Y'] = dfTriad[key_y]
dfTriad_data['Z'] = dfTriad[key_z]
#create list of tuples from the constructed dataframe
triad_data = [tuple(x) for x in dfTriad_data.to_records(index=False)]
plt.subplot(2, 3, i)
tax.scatter(triad_data, marker='D', color='green', label="")
tax.show()
I had the same problem and could solve it by first "going" into the subplot, then creating the ternary figure in there by giving plt.gca() as keyword argument ax:
plt.subplot(2,2,4, frameon = False)
scale = 10
plt.gca().get_xaxis().set_visible(False)
plt.gca().get_yaxis().set_visible(False)
figure, tax = ternary.figure(ax = plt.gca(), scale = scale)
#now you can use ternary normally:
tax.line(scale * np.array((0.5,0.5,0.0)), scale*np.array((0.0, 0.5, 0.5)))
tax.boundary(linewidth=1.0)
#...

Number of legend entries equals the size of data set

I'm plotting many sets of data in a for loop. The number of sets and size of sets don't have any problems plotting. When I try to add a legend, things get interesting. I get a legend, but I only get the first label to show up hundreds of times! I have one data set with 887 points, I get 887 legend entries.Here is the plot I get
You can access the .py and .xlsx files here:
https://drive.google.com/drive/folders/1QCVw2yqIHexNCvgz4QQfJQDGYql1hGW8?usp=sharing
Here is the code that is generating the plot.
# Temperature Data plotting
=================================================
#initialize figure
plt.figure(figsize=(11,8))
Color = 'C'
Marks = '*','o','+','x','s','d','.'
nm = len(Marks)
q = 0 # Marks counter
c = 0 # color counter
for k in range(0,nt):
style = 'C' + str(c) + Marks[q]
test = 'T' + str(k)
plt.plot([t+t_adjust[k]],[Temps[:,k]],style,label=test)
#, label = 'test'
c += 1
if(c==6):
c = 9
if(c==10):
c = 0
q += 1
if(k > nt-10):
q = nm - 1
# Formatting Figure
#names = '1','2','3','4','5'
#name1 = '1'
#pylab.legend([name1])
#from collections import OrderedDict
#import matplotlib.pyplot as plt
#handles, labels = plt.gca().get_legend_handles_labels()
#by_label = OrderedDict(zip(labels, handles))
#plt.legend(by_label.values(), by_label.keys())
plt.legend(loc = 'upper right')
plt.show()
# x axis limits, in seconds
plt.xlim(0,60)
plt.xlabel('t (s)')
plt.ylabel('T (deg C)')
FigTitle = (oper_name + '; ' + str(pres_val) + pres_unit + '; d=' +
str(diam_val) + diam_unit + '; H=' + str(dist_val) + dist_unit)
plt.title(FigTitle)
# End Temperature Data Plotting
==============================================
I have 14 sets of data, with 887 points each. There is clearly more than 14 legend entries. Not sure why its somehow referencing the length of data or something. I found this (code below) to find the handles and labels, but I need them to be assigned the style name for each data set instead of the first style name for the length of data.
#from collections import OrderedDict
#import matplotlib.pyplot as plt
#handles, labels = plt.gca().get_legend_handles_labels()
#by_label = OrderedDict(zip(labels, handles))
#plt.legend(by_label.values(), by_label.keys())
Hard to say without having a look at the data, but you can always control what goes into the legend manually like so:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0., 2*np.pi, 101, endpoint=True)
lns = []
for i in range(1, 10):
for j in range(10):
ln = plt.plot(x, j*np.sin(x/i), label="series i={:d}".format(i))
lns += ln # note plt.plot returns a list of entities
plt.legend(lns, [l.get_label() for l in lns], loc="best")
plt.show()

Changing the colour of a boxplot when its paired

I want to change the colour of the boxplots according to what they represent, this are grouped in pairs, so my question is:
How can i change the colour of the boxplots when they are paired?
Considering that the first boxplot of each pair should be blue and the second one red.
This is the code, sorry if it's messy:
def obtenerBoxplotsAnuales(self, directorioEntrada, directorioSalida):
meses = ["Enero","Febrero","Marzo","Abril","Mayo","Junio", "Julio", "Agosto","Septie.","Octubre","Noviem.","Diciem."]
ciudades = ["CO","CR"]
anios = ["2011", "2012", "2013"]
boxPlotMensual = []
fig = plt.figure()
fig.set_size_inches(14.3, 9)
ax = plt.axes()
plt.hold(True)
for anio in anios:
boxPlotAnual = []
i=0
ticks = []
for mes in range(len(meses)):
data1 = getSomeData()
data2 = getSomeData()
data = [ [int(float(data1[2])), int(float(data1[0])), int(float(data1[1]))],
[int(float(data2[2])), int(float(data2[0])), int(float(data2[1]))] ]
plt.boxplot(data, positions=[i,i+1], widths=0.5)
ticks.append(i+0.5)
i=i+2
hB, = plt.plot([1,1],'b-')
hR, = plt.plot([1,1],'r-')
plt.legend((hB, hR),('Caleta', 'Comodoro'))
hB.set_visible(False)
hR.set_visible(False)
ax.set_xticklabels(meses)
ax.set_xticks(ticks)
plt.savefig(directorioSalida+"/asdasd"+str(anio)+".ps", orientation='landscape', papertype='A4' )
This is what i get:
I've read that the solution is related with the fact that plt.boxplot(...) returns a kind of dict object that contains a list of the lines created so the way to modify the colour of each boxplot would be access to the indexes? How for this case?
You can set the colour of the return dict from boxplot as follows,
import matplotlib.pyplot as plt
import numpy as np
nboxes = 10
# fake up some data
spread= np.random.rand(50,nboxes) * 100
center = np.ones((25,nboxes)) * 50
flier_high = np.random.rand(10,nboxes) * 100 + 100
flier_low = np.random.rand(10,nboxes) * -100
data =np.concatenate((spread, center, flier_high, flier_low), 0)
# plot figure
plt.figure()
bp = plt.boxplot(data)
for i, box in enumerate(bp['boxes']):
#Colour alternate boxes blue and red
if i%2:
box.set_color('blue')
else:
box.set_color('red')
plt.show()
Where you loop through all boxes in bp['boxes'] and use the method set_color (you can also box.set_markerfacecolor and other standard matplotlib artist attributes). The bp dict also contains ['boxes', 'fliers', 'medians', 'means', 'whiskers', 'caps'] which can also be changed as required.

Categories