How to set major locator of secondary axis - python

I want to set a major locator for a secondary axis with 24 hour intervals, but it’s not valid and does not result in any errors.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
dt=pd.DataFrame({'time':[100000,200000,500000,800000],'value':[1,2,4,6]})
plot= plt.subplot()
plot.plot(dt.time,dt.value)
x_major_locator=plt.MultipleLocator(100000)
plot.xaxis.set_major_locator(x_major_locator)
plot.set_xlabel("Second")
s2h=lambda s: s/3600
h2s=lambda h: h*3600
ax2=plot.secondary_xaxis("top",functions=(s2h,h2s))
x_major_locator=plt.MultipleLocator(24)
ax2.xaxis.set_major_locator(x_major_locator)
ax2.set_xlabel("Hour")
plt.show()

I am not sure why the ticks are not being modified; however, one way to get around this is to create a new subplot axis that shares y. The following will work as long as you do not change the limits because the lines are plotted over each other. If do need to change the limits, then you can do a hacky approach by plotting the line in negative y space and setting the ylims which will preserve your top x-axis.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
dt=pd.DataFrame({'time':[100000,200000,500000,800000],'value':[1,2,4,6]})
plot= plt.subplot()
plot.plot(dt.time,dt.value)
x_major_locator=MultipleLocator(100000)
plot.xaxis.set_major_locator(x_major_locator)
plot.set_xlabel("Second")
s2h=lambda s: s/3600
h2s=lambda h: h*3600
#ax2=plot.secondary_xaxis("top",functions=(s2h,h2s))
ax2 = plot.twiny()
ax2.plot(s2h(dt.time),dt.value)
x_major_locator = MultipleLocator(24)
ax2.xaxis.set_major_locator(x_major_locator)
ax2.set_xlabel("Hour")
#ax2.set_xlim(0,200) #If you do this, you get 2 lines
plt.show()

Related

How to remove the first and last minor tick month labels on matplotlib?

I want to generate a chart with the 12 months of a year as the x-axis labels, i.e. 'Jan' to 'Dec', positioned in the middle between the major ticks. I used the code from https://matplotlib.org/3.4.3/gallery/ticks_and_spines/centered_ticklabels.html to create the x-axis. The x-axis created has an additional 'Dec' on the left and 'Jan' on the right, i.e. a total of 14 labels instead of 12 (see attached image). However, only 'Jan' to 'Dec' are wanted on the chart. I would like to know how to remove the 'Dec' label on the left and 'Jan' label on the right? My google searches were only successful with solutions to remove all minor tick labels. Any help will be much appreciated.
I use the following code to generate the chart:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
df = pd.DataFrame(np.random.randint(0,100,size=(365, 2)), columns=list('AB'))
df.index = pd.date_range(start='1/1/2022', end='12/31/2022').strftime('%b-%d')
plt.figure()
ax = plt.gca()
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_minor_locator(mdates.MonthLocator(bymonthday=16))
ax.xaxis.set_major_formatter(ticker.NullFormatter())
ax.xaxis.set_minor_formatter(mdates.DateFormatter('%b'))
for tick in ax.xaxis.get_minor_ticks():
tick.tick1line.set_markersize(0)
tick.tick2line.set_markersize(0)
tick.label1.set_horizontalalignment('center')
plt.plot(df['A'], linewidth=0.5, color='tab:red')
plt.show()
enter image description here
Try setting your x-axis limit to values between 0 and 365. Sometimes matplotlib uses values a little outside of your data. This way, the first Dec and last Jan are automatically eliminated from the plot.
Here I modified your code with 1 argument: plt.xlim(0,365)
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
df = pd.DataFrame(np.random.randint(0,100,size=(365, 2)), columns=list('AB'))
df.index = pd.date_range(start='1/1/2022', end='12/31/2022').strftime('%b-%d')
plt.figure()
ax = plt.gca()
ax.xaxis.set_major_locator(mdates.MonthLocator())
ax.xaxis.set_minor_locator(mdates.MonthLocator(bymonthday=16))
ax.xaxis.set_major_formatter(ticker.NullFormatter())
ax.xaxis.set_minor_formatter(mdates.DateFormatter('%b'))
for tick in ax.xaxis.get_minor_ticks():
tick.tick1line.set_markersize(0)
tick.tick2line.set_markersize(0)
tick.label1.set_horizontalalignment('center')
plt.xlim(0,365)
plt.plot(df['A'], linewidth=0.5, color='tab:red')
plt.show()

How to make horizontal linechart with categorical variables and timeseries?

I want to replicate plots from this paper: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5000555/pdf/nihms774453.pdf I'm particularly interested in plot on page 16, right panel. I tried to do this in matplotlib but it seems to me that there is no way to access lines in linecollection.
I don't know how to change the color of the each line, according to the value at every index. I'd like to eventually get something like here: https://matplotlib.org/3.1.1/gallery/lines_bars_and_markers/multicolored_line.html but for every line, according to the data.
this is what I tried:
the data in numpy array: https://pastebin.com/B1wJu9Nd
import pandas as pd, numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib import colors as mcolors
%matplotlib inline
base_range = np.arange(qq.index.max()+1)
fig, ax = plt.subplots(figsize=(12,8))
ax.set_xlim(qq.index.min(), qq.index.max())
# ax.set_ylim(qq.columns[0], qq.columns[-1])
ax.set_ylim(-5, len(qq.columns) +5)
line_segments = LineCollection([np.column_stack([base_range, [y]*len(qq.index)]) for y in range(len(qq.columns))],
cmap='viridis',
linewidths=(5),
linestyles='solid',
)
line_segments.set_array(base_range)
ax.add_collection(line_segments)
axcb = fig.colorbar(line_segments)
plt.show()
my result:
what I want to achieve:

Ticklabels in matplotlib don't match the plot values [duplicate]

I have an existing plot that was created with pandas like this:
df['myvar'].plot(kind='bar')
The y axis is format as float and I want to change the y axis to percentages. All of the solutions I found use ax.xyz syntax and I can only place code below the line above that creates the plot (I cannot add ax=ax to the line above.)
How can I format the y axis as percentages without changing the line above?
Here is the solution I found but requires that I redefine the plot:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.ticker as mtick
data = [8,12,15,17,18,18.5]
perc = np.linspace(0,100,len(data))
fig = plt.figure(1, (7,4))
ax = fig.add_subplot(1,1,1)
ax.plot(perc, data)
fmt = '%.0f%%' # Format you want the ticks, e.g. '40%'
xticks = mtick.FormatStrFormatter(fmt)
ax.xaxis.set_major_formatter(xticks)
plt.show()
Link to the above solution: Pyplot: using percentage on x axis
This is a few months late, but I have created PR#6251 with matplotlib to add a new PercentFormatter class. With this class you just need one line to reformat your axis (two if you count the import of matplotlib.ticker):
import ...
import matplotlib.ticker as mtick
ax = df['myvar'].plot(kind='bar')
ax.yaxis.set_major_formatter(mtick.PercentFormatter())
PercentFormatter() accepts three arguments, xmax, decimals, symbol. xmax allows you to set the value that corresponds to 100% on the axis. This is nice if you have data from 0.0 to 1.0 and you want to display it from 0% to 100%. Just do PercentFormatter(1.0).
The other two parameters allow you to set the number of digits after the decimal point and the symbol. They default to None and '%', respectively. decimals=None will automatically set the number of decimal points based on how much of the axes you are showing.
Update
PercentFormatter was introduced into Matplotlib proper in version 2.1.0.
pandas dataframe plot will return the ax for you, And then you can start to manipulate the axes whatever you want.
import pandas as pd
import numpy as np
df = pd.DataFrame(np.random.randn(100,5))
# you get ax from here
ax = df.plot()
type(ax) # matplotlib.axes._subplots.AxesSubplot
# manipulate
vals = ax.get_yticks()
ax.set_yticklabels(['{:,.2%}'.format(x) for x in vals])
Jianxun's solution did the job for me but broke the y value indicator at the bottom left of the window.
I ended up using FuncFormatterinstead (and also stripped the uneccessary trailing zeroes as suggested here):
import pandas as pd
import numpy as np
from matplotlib.ticker import FuncFormatter
df = pd.DataFrame(np.random.randn(100,5))
ax = df.plot()
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: '{:.0%}'.format(y)))
Generally speaking I'd recommend using FuncFormatter for label formatting: it's reliable, and versatile.
For those who are looking for the quick one-liner:
plt.gca().set_yticklabels([f'{x:.0%}' for x in plt.gca().get_yticks()])
this assumes
import: from matplotlib import pyplot as plt
Python >=3.6 for f-String formatting. For older versions, replace f'{x:.0%}' with '{:.0%}'.format(x)
I'm late to the game but I just realize this: ax can be replaced with plt.gca() for those who are not using axes and just subplots.
Echoing #Mad Physicist answer, using the package PercentFormatter it would be:
import matplotlib.ticker as mtick
plt.gca().yaxis.set_major_formatter(mtick.PercentFormatter(1))
#if you already have ticks in the 0 to 1 range. Otherwise see their answer
I propose an alternative method using seaborn
Working code:
import pandas as pd
import seaborn as sns
data=np.random.rand(10,2)*100
df = pd.DataFrame(data, columns=['A', 'B'])
ax= sns.lineplot(data=df, markers= True)
ax.set(xlabel='xlabel', ylabel='ylabel', title='title')
#changing ylables ticks
y_value=['{:,.2f}'.format(x) + '%' for x in ax.get_yticks()]
ax.set_yticklabels(y_value)
You can do this in one line without importing anything:
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{}%'.format))
If you want integer percentages, you can do:
plt.gca().yaxis.set_major_formatter(plt.FuncFormatter('{:.0f}%'.format))
You can use either ax.yaxis or plt.gca().yaxis. FuncFormatter is still part of matplotlib.ticker, but you can also do plt.FuncFormatter as a shortcut.
Based on the answer of #erwanp, you can use the formatted string literals of Python 3,
x = '2'
percentage = f'{x}%' # 2%
inside the FuncFormatter() and combined with a lambda expression.
All wrapped:
ax.yaxis.set_major_formatter(FuncFormatter(lambda y, _: f'{y}%'))
Another one line solution if the yticks are between 0 and 1:
plt.yticks(plt.yticks()[0], ['{:,.0%}'.format(x) for x in plt.yticks()[0]])
add a line of code
ax.yaxis.set_major_formatter(ticker.PercentFormatter())

how to reduce y-axis in matplot with same distance

I want this plot's y-axis to be centered at 38, and the y-axis scaled such that the 'humps' disappear. How do I accomplish this?
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
s=['05/02/2019', '06/02/2019', '07/02/2019', '08/02/2019',
'09/02/2019', '10/02/2019', '11/02/2019', '12/02/2019',
'13/02/2019', '20/02/2019', '21/02/2019', '22/02/2019',
'23/02/2019', '24/02/2019', '25/02/2019']
df[0]=['38.02', '33.79', '34.73', '36.47', '35.03', '33.45',
'33.82', '33.38', '34.68', '36.93', '33.44', '33.55',
'33.18', '33.07', '33.17']
# Data for plotting
fig, ax = plt.subplots(figsize=(17, 2))
for i,j in zip(s,df[0]):
ax.annotate(str(j),xy=(i,j+0.8))
ax.plot(s, df[0])
ax.set(xlabel='Dates', ylabel='Latency',
title='Hongkong to sing')
ax.grid()
#plt.yticks(np.arange(min(df[p]), max(df[p])+1, 2))
fig.savefig("test.png")
plt.show()
I'm not entirely certain if this is what you're looking for but you can adjust the y-limits explicitly to change the scale, i.e.
ax.set_ylim([ax.get_ylim()[0], 42])
Which only sets the upper bound, leaving the lower limit unchanged, this would give you
you can supply any values you find appropriate, i.e.
ax.set_ylim([22, 52])
will give you something that looks like
Also note that the tick labels and general appearance of your plot will differ from what is shown here.
Edit - Here is the complete code as requested:
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
df = pd.DataFrame()
s=['05/02/2019', '06/02/2019', '07/02/2019', '08/02/2019',
'09/02/2019', '10/02/2019', '11/02/2019', '12/02/2019',
'13/02/2019', '20/02/2019', '21/02/2019', '22/02/2019',
'23/02/2019', '24/02/2019', '25/02/2019']
df[0]=['38.02','33.79','34.73','36.47','35.03','33.45',
'33.82','33.38','34.68','36.93','33.44','33.55',
'33.18','33.07','33.17']
# Data for plotting
fig, ax = plt.subplots(figsize=(17, 3))
#for i,j in zip(s,df[0]):
# ax.annotate(str(j),xy=(i,j+0.8))
ax.plot(s, pd.to_numeric(df[0]))
ax.set(xlabel='Dates', ylabel='Latency',
title='Hongkong to sing')
ax.set_xticklabels(pd.to_datetime(s).strftime('%m.%d'), rotation=45)
ax.set_ylim([22, 52])
plt.show()

Python - Matplotlib - Setting X axis range - Plotting Packets Per Second

I'm working on a script that plots a pps count versus time from a csv file. Everything works up to this point however I can't seem to figure out how to change the interval at which the ticks/tick-labels occur at on the X-axis, I want there to be 60 timestamps/tick instead of the default. Here's where I'm at:
import matplotlib
matplotlib.use('Agg')
from matplotlib.mlab import csv2rec
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator
from pylab import *
data = csv2rec('tpm_counter.log', names=['packets', 'time']) # reads in the data from the csv as column 1 = tweets column 2 = time
rcParams['figure.figsize'] = 12, 4 # this sets the ddimensions of the graph to be made
rcParams['font.size'] = 8
fig = plt.figure()
plt.plot(data['time'], data['packets']) # this sets the fields to be graphed
plt.xlabel("Time(minutes)") # this sets the x label
plt.ylabel("Packets") # this sets the y label
plt.title("Packets Capture Log: Packets Per Minute") # this sets the title
#plt.xticks(range(60)) --- nothing shows on the graph if I use this
fig.autofmt_xdate(bottom=0.2, rotation=90, ha='left')
plt.savefig('tpm.png') # this sets the output file name
I've tried plt.xticks(range(60)) but when the plot generates, it has nothing on it.
bmu's answer above works. But it might be helpful to others to see a more general way of rescaling the xticks and xlabels in a plot. I have generated some example data instead of using a csv file.
import matplotlib
import matplotlib.pyplot as plt
from pylab import *
time=range(5000) #just as an example
data=range(5000) # just as an example
fig = plt.figure()
plt.plot(time,data) # this sets the fields to be graphed
plt.xlabel("Every 60th point") # this sets the x label
plt.ylabel("Data") # this sets the y label
plt.title("Rescaling axes") # this sets the title
#Slice the data into every 60th point. We want ticks at these points
tickpos=data[::60]
#Now create a list of labels for each point...
ticklabels=[]
for point in tickpos:
ticklabels.append(str(point/60))
plt.xticks(tickpos,ticklabels) # set the xtick positions and labels
plt.savefig('tpm.png')
Have a look at the date demo.
You can use the HourLocator or the MinuteLocator together with an adapted DateFormatter.
import matplotlib.dates as mdates
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot_date(data['time'], data['packets'])
hours = mdates.HourLocator()
fmt = mdates.DateFormatter('%H:%M')
ax.xaxis.set_major_locator(hours)
ax.xaxis.set_major_formatter(fmt)

Categories