I am pretty new to Seaborn so that may sounds like a stupid question but I want to change ticks in my X-axis (which is a date) on a lineplot.
I have created a graph as follows:
import seaborn as sns
import matplotlib.pyplot as plt
g = sns.lineplot(data=to_plot.loc[ref_date:])
But I get this result:
Obviously the dates cannot be read so I would like to have one tick for every 7 days.
How to do that?
This is actually changed via Matplotlib, as Seaborn is a kind of wrapper around it.
Also, sns.lineplot return a matplotlib.axes._subplots.AxesSubplot object.
This is what you can do
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
loc = plticker.MultipleLocator(base=7.0) # To put a tick every seven days
# plt.figure(figsize=(15, 6)) # to change the size of the graph
# plt.title(f"Some super title")
# plt.ylabel("% active cases") # if you want to add a label in y-axis
axes = sns.lineplot(data=to_plot.loc[REF_DATE:])
axes.xaxis.set_major_locator(loc)
The result can look like this
Related
I have used FacetGrid() from the seaborn module to break a line graph into segments with labels for each region as the title of each subplot. I saw the option in the documentation to have the x-axes be independent. However, I could not find anything related to having the plot sizes correspond to the size of each axis.
The code I used to generate this plot, along with the plot, are found below.
import matplotlib.pyplot as plt
import seaborn as sns
# Added during Edit 1.
sns.set()
graph = sns.FacetGrid(rmsf_crys, col = "Subunit", sharex = False)
graph.map(plt.plot, "Seq", "RMSF")
graph.set_titles(col_template = '{col_name}')
plt.show()
Plot resulting from the above code
Edit 1
Updated plot code using relplot() instead of calling FacetGrid() directly. The final result is the same graph.
import matplotlib.pyplot as plt
import seaborn as sns
# Forgot to include this in the original code snippet.
sns.set()
graph = sns.relplot(data = rmsf_crys, x = "Seq", y = "RMSF",
col = "Subunit", kind = "line",
facet_kws = dict(sharex=False))
graph.set_titles(col_template = '{col_name}')
plt.show()
Full support for this would need to live at the matplotlib layer, and I don't believe it's currently possible to have independent axes but shared transforms. (Someone with deeper knowledge of the matplotlib scale internals may prove me wrong).
But you can get pretty close by calculating the x range you'll need ahead of time and using that to parameterize the gridspec for the facets:
import numpy as np, seaborn as sns
tips = sns.load_dataset("tips")
xranges = tips.groupby("size")["total_bill"].agg(np.ptp)
xranges *= 1.1 # Account for default margins
sns.relplot(
data=tips, kind="line",
x="total_bill", y="tip",
col="size", col_order=xranges.index,
height=3, aspect=.65,
facet_kws=dict(sharex=False, gridspec_kws=dict(width_ratios=xranges))
)
I am trying to generate a matplotlib plot that consists of two subplots that share an x-axis.
One subplot plots two sets of data on different y-axis scales, and the other just plots one set of data.
I would also like to "break" the x-axis of both subplots to account for a large period of time in between measurements in my data.
I have tried using the solution presented in this post, but as one poster claims, this method does not properly scale subplot x-axes.
I have been trying to use the brokenaxes package. It seems to create multiple subplots, scale them accordingly, and hide extraneous spines, ticks, and tick labels automatically.
This package doesn't seem to support sharing an x-axis (sharex) with subplots created from GridSpec.
Additionally, the method twinx does not work natively on a brokenaxes object. However each brokenaxes object contains a list of subplot axes objects. Each axes object can be used to generate a twinx, although this results in matplotlib rescaling subplots, redrawing spines, tick marks, and labels.
I have attempted to manually hide all the redrawn elements, however I'm still having trouble with a few things.
The lines that denote the broken axis are placed in an incorrect location
I have left y-axis tick marks on the top right subplot that I can't hide
The gridlines on the top right subplot are incorrect and inconsistent with the x-axis on the bottom subplot.
I want to use a date formatter using '%m-%d' for the x-axis ticks, but I'm getting an error attempting it:
AttributeError: 'CallCurator' object has no attribute 'set_major_formatter
Here is the result of my attempts:
And the pickled pandas data and code
used to generate it:
#!/usr/bin/env python3
import matplotlib as mpl
font= {'family': 'Arial',
'size': 7}
mpl.rc('font', **font)
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
from matplotlib.ticker import NullFormatter
import matplotlib.dates as md
from brokenaxes import brokenaxes
import datetime
from pytz import timezone
import numpy as np
from pytz import timezone
import pandas as pd
from glob import glob
volt_color= 'tab:blue'
volt_style = 'solid'
amp_color = 'tab:red'
amp_style='dashed'
# starting date
start = pd.to_datetime("2020-05-20 15:40:09.059089184-07:00", infer_datetime_format=True).to_pydatetime()
start = timezone('US/Pacific').localize(start)
# load data
mudbat_data = pd.read_pickle("mudbat_data.pkl")
# perform rolling mean over data
mv = mudbat_data.rolling(5*60).mean()
# instantiate figures, and broken axes subplots
fig = plt.figure(figsize=(4,2))
gs = GridSpec(2,1)
tz = timezone('US/Pacific')
print(start)
[x1, x2, x3, x4] = [start, datetime.datetime(2020,5,27,tzinfo=tz), datetime.datetime(2020,7,20,tzinfo=tz),datetime.datetime(2020,7,22,tzinfo=tz)]
bax1 = brokenaxes(xlims=((x1,x2),(x3,x4)), subplot_spec=gs[0])
bax3 = brokenaxes(xlims=((x1,x2),(x3,x4)), subplot_spec=gs[1])
# plot first data
bax1.set_ylabel('Cell Voltage (V)')
bax1.plot(mv.index, mv['voltage'], color=volt_color, ls=volt_style)
bax1.tick_params(axis='y', labelcolor=volt_color)
bax1.grid(True)
# ensure all extraneous ticks for bax1 are hidden
bax1.axs[0].yaxis.tick_left()
bax1.axs[0].xaxis.set_ticklabels([])
bax1.axs[0].xaxis.set_ticklabels([])
bax1.axs[0].xaxis.set_ticks_position('none')
bax1.axs[0].yaxis.set_ticks_position('none')
bax1.axs[1].xaxis.set_ticklabels([])
bax1.axs[1].yaxis.set_ticklabels([])
bax1.axs[1].xaxis.set_ticks_position('none')
bax1.axs[1].yaxis.set_ticks_position('none')
# generate bax2 from bax1 axes
bax2 = []
for ax in bax1.axs:
bax2.append(ax.twinx())
# plot data on bax2 subplots
bax2[0].plot(mv.index, -1E6*mv['current'], color=amp_color, ls=amp_style)
bax2[1].plot(mv.index, -1E6*mv['current'], color=amp_color, ls=amp_style)
bax2[1].set_ylabel('Harvesting Current (μA)')
bax2[1].tick_params(axis='y', labelcolor=amp_color)
# hide generated spines and ticks/labels
bax2[0].spines['right'].set_visible(False)
bax2[0].yaxis.set_ticklabels([])
bax2[0].xaxis.set_ticklabels([])
bax2[0].xaxis.set_ticks_position('none')
bax2[0].yaxis.set_ticks_position('none')
bax2[1].spines['left'].set_visible(False)
bax2[1].xaxis.set_ticklabels([])
bax2[1].xaxis.set_ticks_position('none')
bax2[1].yaxis.tick_right()
# I would like to use this formatter
#bax3.xaxis.set_major_formatter(md.DateFormatter('%m-%d'))
bax3.set_ylabel("Power (uW)")
bax3.grid(True)
bax3.plot(mv.index, 1E6*mv['power'])
bax3.tick_params(axis='x', labelsize=6, rotation=45)
plt.tight_layout(pad=0.4, w_pad=0.5, h_pad=0.5)
plt.subplots_adjust(hspace=0)
plt.savefig('plot.png', dpi=300)
Thank you for any help or suggestions you can give!
I would like to create a similar graph, in this case China and USA have huge PIB that I cant see the details for small countries
For this, you can use ax.set_xscale:
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
plt.scatter(your_data)
ax = plt.gca() #get current axis object
ax.set_xscale('log') #you can use 'symlog' if your data is close to 0
ax.set_yscale('log')
#if you don't want to show the ticks in scientific notation
for axis in [ax.xaxis, ax.yaxis]:
formatter = ScalarFormatter()
formatter.set_scientific(False)
axis.set_major_formatter(formatter)
I am trying to make a line plot using seaborn and in the image link I have attached
it seems like it did not show the required dates (daily) in the x-axis. How can I fix this chart?
%matplotlib inline
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.style.use(['ggplot'])
import seaborn as sns
sns.set_style("whitegrid")
fig, g = plt.subplots(figsize = (20,6))
g = sns.lineplot(x="photosim_date", y="tdpower_mean", hue="tool_id", style="tool_id", data=df1, dashes=False, ax=g)
plt.ylim(80,140)
plt.title("L8 PhotoSIM SDET TDP Data")
plt.show(g)
You can change the x-axis tick locator.
loc = matplotlib.dates.DayLocator(bymonthday=range(1,32))
ax.xaxis.set_major_locator(loc)
Be wary that the axis labels will most likely overlap in this case. You can use fig.autofmt_xdate() to automatically rotate the labels.
I'm building a Seaborn barplot. The x-axis are dates, and the y-axis are integers.
I'd like to format major/minor ticks for the dates. I'd like Mondays' ticks to be bold and a different color (ie, "major ticks"), with the rest of the week less bold.
I have not been able to get major and minor tick formatting on the x-axis to work with Seaborn barplots. I'm stumped, and thus turning here for help.
I'm starting with the stackoverflow example that answered this question: Pandas timeseries plot setting x-axis major and minor ticks and labels
If I do a simple modification it to use a Seaborn barplot and I lose my X-axis ticks:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import seaborn as sns
idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)
###########################################
## Swap out these two lines of code:
#fig, ax = plt.subplots()
#ax.plot_date(idx.to_pydatetime(), s, 'v-')
## with this one
ax = sns.barplot(idx.to_pydatetime(), s)
###########################################
ax.xaxis.set_minor_locator(dates.WeekdayLocator(byweekday=(1),
interval=1))
ax.xaxis.set_minor_formatter(dates.DateFormatter('%d\n%a'))
ax.xaxis.grid(True, which="minor")
ax.yaxis.grid()
ax.xaxis.set_major_locator(dates.MonthLocator())
ax.xaxis.set_major_formatter(dates.DateFormatter('\n\n\n%b\n%Y'))
plt.tight_layout()
## save the result to a png instead of plotting to screen:
myFigure = plt.gcf()
myFigure.autofmt_xdate()
myFigure.set_size_inches(11,3.8)
plt.title('Example Chart', loc='center')
plt.savefig('/tmp/chartexample.png', format='png', bbox_inches='tight')
I've tried a variety of approaches but something in Seaborn seems to be overriding or undoing any attempts at major and minor axis formatting that I've managed to cook up yet beyond some simple styling for all ticks when I use set_xticklabels().
I can sort of get formatting on just the major ticks by using MultipleLocator(), but I can't get any formatting on the minor ticks.
I've also experimented with myFigure.autofmt_xdate() to see if it would help, but it doesn't seem to like mixed major & minor ticks on the same axis either.
I came across this while trying to solve the same problem. Based on the useful pointer from #mwaskom (that categorical plots like boxplots lose their structure and just become date-named categories) and ended up doing the location and formatting in Python as so:
from datetime import datetime
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import seaborn as sns
idx = pd.date_range('2011-05-01', '2011-07-01')
s = pd.Series(np.random.randn(len(idx)), index=idx)
fig, ax = plt.subplots(figsize = (12,6))
ax = sns.barplot(idx.to_pydatetime(), s, ax = ax)
major_ticks = []
major_tick_labels = []
minor_ticks = []
minor_tick_labels = []
for loc, label in zip(ax.get_xticks(), ax.get_xticklabels()):
when = datetime.strptime(label.get_text(), '%Y-%m-%d %H:%M:%S')
if when.day == 1:
major_ticks.append(loc)
major_tick_labels.append(when.strftime("\n\n\n%b\n%Y"))
else:
minor_ticks.append(loc)
if when.weekday() == 0:
minor_tick_labels.append(when.strftime("%d\n%a"))
else:
minor_tick_labels.append(when.strftime("%d"))
ax.set_xticks(major_ticks)
ax.set_xticklabels(major_tick_labels)
ax.set_xticks(minor_ticks, minor=True)
ax.set_xticklabels(minor_tick_labels, minor=True)
Of course, you don't have to set the ticks based on parsing the labels which were installed from the data, if it's easier to start with the source data and just keep the indices aligned, but I prefer to have a single source of truth.
You can also mess with font weight, rotation, etc, on individual labels by getting the Text objects for the relevant label and calling set_ methods on it.