Draw horizontal lines (matplotlib) - python

I try to run the code:
fig, ax = plt.subplots()
ax.plot(x, y, color="g")
ax.xaxis.set_major_locator(matplotlib.dates.YearLocator())
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%Y'))
hlines=[40,50]
ax.hlines(hlines, 1, len(x), color='g')
plt.show()
I expect it to draw something like this:
Drawing chart is not a problem, it works, but drawing horizontal lines doesn't work.
When I run my code it draws:
P.S. x created this way: dates to matplotlib dates
x.append(matplotlib.dates.date2num(datetime.strptime(date, '%Y%m%d')))

You are drawing a horizontal line from x-axis=1 to x-axis=len(x), which are just arbitrary integers that does not represent anything on your graph: your x-axis is much larger because you use matplotlib.dates.date2num. You need to properly assign the range for your horizontal line. For example:
ax.hlines(hlines, min(x), max(x), color='g')
or
ax.hlines(hlines,
matplotlib.dates.date2num(datetime.strptime(mindate, '%Y%m%d')),
matplotlib.dates.date2num(datetime.strptime(maxdate, '%Y%m%d')),
color='g')
or you could just use axhline:
ax.axhline(40, color='g')
ax.axhline(50, color='g')

Related

matplotlib break line near a marker

Is there are posibility to break a line in a matplotlib diagram. I made a sketch for better understanding what I mean.
If by 'intercept' you mean the gaps or buffers between the points and the lines, I'd add an edge around the points like so:
import matplotlib.pyplot as plt
x = y = [1, 2, 3]
fig, ax = plt.subplots()
ax.plot(x, y, lw=5, c='k') # The black line.
ax.scatter(x, y, s=200, c='lime', ec='white', lw=5, zorder=10)
ax.set_xlim(0.5, 3.5)
ax.set_ylim(0.5, 3.5)
This prodices:
I'm plotting the line and points separately, controlling the layer order with zorder. There are always lots of ways to do things, but I find markers easier to control with plt.scatter().

How to plot two series with very different scales in python

I'm a beginner in python. I have to plot two graphs in the same plot. One of my graphs is velocity, which ranges between (-1,1), and the other one is groundwater, which ranges between (10,12). When I use the following code, the graphs become very small.
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
df=pd.read_excel ('final-all-filters-0.6.xlsx')
df['Date']=pd.to_datetime(df['Date'])
date = df['Date']
gwl = df['gwl']
v =df['v']
plt.plot(date,gwl, color='deepskyblue',linewidth=2)
plt.plot(date,v, color='black',linewidth=2)
ax1.grid(axis='y')
ax1.xaxis.set_major_locator(matplotlib.dates.YearLocator())
ax1.xaxis.set_minor_locator(matplotlib.dates.MonthLocator((1,3,5,7,9,11)))
ax1.xaxis.set_major_formatter(matplotlib.dates.DateFormatter("\n%Y"))
ax1.xaxis.set_minor_formatter(matplotlib.dates.DateFormatter("%b"))
ax1.grid(which='minor', alpha=0.3, linestyle='--')
ax1.grid(which='major', alpha=2)
for spine in ax1.spines.values():
spine.set_edgecolor('gray')
ax1.tick_params(axis='x', which='both', colors='gray')
ax1.tick_params(axis='y', colors='gray')
ax1.set_ylabel('v', color='g')
ax2.set_ylabel('GWL', color='b')
plt.show()
But when I add the ax1.set_ylim(-1, 1)and ax2.set_ylim(10, 12) to my code, one of the graph was disappered!
I think it does plot the black graph, but it's out of range. You can check that by adding 11 or something to the black plot value.
Maybe you can try using ax2.set_yticks(np.arange(-1, 1, 0.5)) instead of set_ylim and/or using ax2.autoscale(enable=True, axis=y)

matplotlib: axes border and tick mark/label locations

The end result I'm attempting to achieve is to have a "thicker" black boarder around my plot, along xmin, xmax, ymin, & ymax. I've tried a couple of different things (such as just drawing a rectangle on the plot, see below), but I have not been able to achieve the desired results for a few reasons.
Because I cannot just use the spines (I've set 2 of them to always be at 0), I need to add some other line or rectangle to create the desired border.
By default the first and last tick labels overhang the axes. I "overcame" this by changing the horizontal or vertical alignment, but they could still use some more padding. I know this is possible, but requires a transform and is a bit clunky.
Now I'd like to remove the first and last tick marks on both axis. This is because given the way the rectangle is drawn it is always inside the plot area, but the first and last tick mark are always outside it, regardless of how thick the rectangle is. Making the rectangle thicker only causes it to overlap the first and last tick label more, which the actual tick mark remains outside the rectangle.
Any other suggestions on how to achieve this kind of border while always maintaining an axis at 0, 0 would be welcomed. That is the overall desired result.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
from matplotlib.patches import Rectangle
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
patPlotBorder = ax.add_artist(Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=2))
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
Without changing much of your code, you can set the clip_on to False, such that the complete rectangle is shown.
border = Rectangle((-10, -10), 20, 20, fill=False, color='k', linewidth=3, clip_on=False)
ax.add_artist(border)
Since the gridlines are shown above the axes content, you have some grey line within the rectangle border.
Alternatively, you can use two axes. One with all the content and the modified spine positions etc., and one where you just make the spines bold and remove all the rest.
import numpy as np
import matplotlib.pyplot as plt
X = np.random.randint(low=-9, high=9, size=10)
Y = np.random.randint(low=-9, high=9, size=10)
fig, ax = plt.subplots()
ax2 = fig.add_subplot(111)
ax2.patch.set_visible(False)
ax2.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
for _, sp in ax2.spines.items():
sp.set_linewidth(3)
ax.axis([-10, 10, -10, 10])
ax.spines['left'].set_position('zero')
ax.spines['bottom'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.setp(ax.xaxis.get_majorticklabels()[0], ha='left')
plt.setp(ax.xaxis.get_majorticklabels()[-1], ha='right')
plt.setp(ax.yaxis.get_majorticklabels()[0], va='bottom')
plt.setp(ax.yaxis.get_majorticklabels()[-1], va='top')
ax.grid(True)
fig.set_tight_layout(True)
ax.scatter(X, Y, c="b", marker="o", s=40)
plt.show()
You can access the individual grid lines by calling get_{x|y}gridlines(). Each grid line is an object of type Line2D, and you can change any of their properties, such as thickness, color, etc.
ax.get_xgridlines()[0].set_linewidth(5)
ax.get_xgridlines()[-1].set_linewidth(5)
ax.get_ygridlines()[0].set_linewidth(5)
ax.get_ygridlines()[-1].set_linewidth(5)

Two y axes for a single plot

I'm trying to create a plot with two Y axes (left and right) for the same data, that is, one is a scaled version of the other. I would like also to preserve the tick positions and grid positions, so the grid will match the ticks at both sides.
I'm trying to do this by plotting twice the same data, one as-is and the other scaled, but they are not coincident.
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(17, 27, 0.1)
y1 = 0.05 * x + 100
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x, y1, 'g-')
ax2.plot(x, y1/max(y1), 'g-')
ax1.set_xlabel('X data')
ax1.set_ylabel('Y data', color='g')
ax2.set_ylabel('Y data normalized', color='b')
plt.grid()
plt.show()
Any help will be appreciated.
Not sure if you can achieve this without getting ugly-looking numbers on your normalized axis. But if that doesn't bother you, try adding this to your code:
ax2.set_ylim([ax1.get_ylim()[0]/max(y1),ax1.get_ylim()[1]/max(y1)])
ax2.set_yticks(ax1.get_yticks()/max(y1))
Probably not the most elegant solution, but it scales your axis limits and tick positions similarly to what you do with the data itself so the grid matches both axes.

Plotting multiple series on a line/bar graph with pandas

I'm trying to make a plot of a line and bar on the same graph. I'm close, but I can't solve a few items. Here's what I have so far...
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.DataFrame({'Value1': np.arange(80, 180, 1),
'Value2': np.arange(1.5, .5, -0.01)},
index=np.arange(10, 110, 1))
fig, ax = plt.subplots(figsize=(10, 10))
data['Value1'].plot(ax=ax)
ax2 = ax.twinx()
data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3))
So the problems I have with this graph are...
The x-ticks look awful. If I only do a line graph, the x-ticks look fine. As soon as I add the twinx axis however, the major/minor ticks logic get's dropped. How can I keep that?
My x-axes is numeric. Note that the line intercepts the x-axis at the value "10" (its hard to see, but that's what's going on). I presume this is because the line's x-axis is supposed to begin at "10" and the bar's x-axis begins at 10 as well, but there's confusion of the value and label so the line's x-axis get's pushed over the label "20".
What's the best way to do this?
Bar plot and line plot has different X coordinate range is different, consider using two x coordinate.
you can try to save xticks and xtickslabels after data['Value1'].plot(ax=ax) and set them back after data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3)):
data['Value1'].plot(ax=ax)
xticks = ax.get_xticks()
xlabels = [x.get_text() for x in ax.get_xticklabels()]
ax2 = ax.twinx()
data['Value2'].plot(kind='bar', ax=ax2, color='y', ylim=(0, 3))
ax.set_xticks(xticks)
ax.set_xticklabels(xlabels)
plt.show()

Categories