I have coded the following program with matplotlib to graph no.of elements with time.
import pylab
import numpy as np
import datetime
from matplotlib.dates import YearLocator, MonthLocator, DateFormatter
date1 = datetime.date(1995, 1, 1)
date2 = datetime.date(2004, 4, 12)
years = YearLocator() # every year
months = MonthLocator() # every month
yearsFmt = DateFormatter('%Y')
ax.xaxis.set_major_locator(years)
ax.xaxis.set_major_formatter(yearsFmt)
ax.xaxis.set_minor_locator(months)
ax.autoscale_view()
pylab.ylim(0, 250)
plt.yticks(np.linspace(0,250,6,endpoint=True))
pylab.xlabel('YEAR')
pylab.ylabel('No. of sunspots')
pylab.title('SUNSPOT VS YEAR GRAPH')
a=[[50,50],[100,100],[250, 250],[200,200],[150,150]]
plt.plot(*zip(*a), marker='o', color='r', ls='')
The output is as follows
However,I was expecting it to display years instead of numbers in x-axis.
You are plotting years, but the years are 50, 100, 250, 200, and 150. These are the first element in the lists inside of a, which is passed to pyplot.plot as the x values.
You want to define your dates somewhere, though you'll also probably want to set the xticks to be the same as the dates you're plotting, as I can tell you care about the graph looking neat.
import pylab
import numpy as np
import datetime
from matplotlib.dates import YearLocator, MonthLocator, DateFormatter
Also, don't forget to import pyplot
import matplotlib.pyplot as plt
Here are some example dates. You can change them to whatever exact date you have for the sunspot measurements.
a=[[datetime.date(1995, 1, 1), 50],
[datetime.date(2000, 1, 1), 100],
[datetime.date(2005, 1, 1), 250],
[datetime.date(2010, 1, 1), 200],
[datetime.date(2015, 1, 1), 150]
]
years = YearLocator() # every year
months = MonthLocator() # every month
yearsFmt = DateFormatter('%Y')
Call gca to get current axis before you modify the axis.
ax = plt.gca()
ax.xaxis.set_major_locator(years)
ax.xaxis.set_major_formatter(yearsFmt)
ax.xaxis.set_minor_locator(months)
ax.autoscale_view()
pylab.ylim(0, 250)
plt.yticks(np.linspace(0,250,6,endpoint=True))
Pick out the dates from the a array to use them as xtick labels.
dates = [date for date,sunspot in a]
plt.xticks(dates)
pylab.xlabel('YEAR')
pylab.ylabel('No. of sunspots')
pylab.title('SUNSPOT VS YEAR GRAPH')
plt.plot(*zip(*a), marker='o', color='r', ls='')
plt.show()
Related
I am struggling to understand why my quiver arrows aren't pointing in the proper direction when plotting wind direction. I have provided an MWE below that has arrays dates, wind_dir, and wind_speed and plots their values over time. I use angles parameter as 'xy' so that arrows point from (x,y) to (x+u,y+v). Anyone have a minute to help?
from matplotlib.dates import date2num
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
from matplotlib.dates import DateFormatter, YearLocator, MonthLocator, DayLocator, HourLo
#from file.py import *
dates = [datetime(2020, 4, 22, 0, 53),
datetime(2020, 4, 22, 1, 53),
datetime(2020, 4, 22, 2, 53),
datetime(2020, 4, 22, 3, 53)]
idx = date2num(dates)
wind_dir = [170., 150., 130., 230.]
wind_speed = [3.6, 2.57, 4.12, 7.09 ]
U = wind_speed*np.cos(wind_dir)
V = wind_speed*np.sin(wind_dir)
fig, ax1 = plt.subplots()
qq = ax1.quiver(idx, np.ones(len(idx))*1,U,V,wind_speed,
pivot='mid',angles='xy',cmap=plt.cm.jet)
plt.colorbar(qq, cmap=plt.cm.jet)
ax1.plot(dates, wind_speed)
# format the x ticks
months = MonthLocator()
days = DayLocator()
hours = HourLocator(byhour=[0,1,2,3])
hours_each = HourLocator() # Find all hours
dateFmt = DateFormatter('%b %d\n%H:%M') # Tick label format style
ax1.xaxis.set_major_locator(hours)
ax1.xaxis.set_major_formatter(dateFmt) # Set the x-axis labels
ax1.xaxis.set_minor_locator(hours_each)
plt.xticks(rotation=30)
plt.show()
I'm trying to fill the area under the curve where the y-value is 1. The x-axis is a datetime array with non-regular values. As you can see the fill also includes areas where there is no x-data. Is there a way to tell fill_between to only fill "between" valid data? i.e. in the plot below I'd like the areas between "missing" samples to be white
tx = array(datetimes) # Array of irregular datetimes
ty = array([ones and zeros]) # Array of ones and zeros same size as tx
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.plot(tx, ty, 'r.')
ax.fill_between(tx, 0, 1, where(ty==1))
This might be what you're aiming for.
If it is then you can use rectangular patches. In this code:
y is the list of values meant to correspond to your 'irregular' pattern.
delta_float measures the horizontal distance in the graph corresponding to delta, the distance between ticks.
Notice that the patches are positioned and sized based on dates and delta_float units respectively.
import datetime
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.dates import DayLocator, HourLocator, DateFormatter, drange, date2num
from numpy import arange
date1 = datetime.datetime(2000, 3, 2)
date2 = datetime.datetime(2000, 3, 6)
delta = datetime.timedelta(hours=6)
dates = drange(date1, date2, delta)
delta_float = (dates[-1]-dates[0])/len(dates)
y = [1,1,0,0,1,1,1,0,1,1,0,0,1,1,0,0]
fig, ax = plt.subplots()
ax.plot_date(dates, y, 'r.')
ax.add_patch(patches.Rectangle((dates[0], 0), delta_float, 1, color='grey'))
ax.add_patch(patches.Rectangle((dates[4], 0), 2*delta_float, 1, color='grey'))
ax.add_patch(patches.Rectangle((dates[8], 0), delta_float, 1, color='grey'))
ax.add_patch(patches.Rectangle((dates[12], 0), delta_float, 1, color='grey'))
ax.xaxis.set_major_locator(DayLocator())
ax.xaxis.set_minor_locator(HourLocator(arange(0, 25, 6)))
ax.xaxis.set_major_formatter(DateFormatter('%Y-%m-%d'))
ax.fmt_xdata = DateFormatter('%Y-%m-%d %H:%M:%S')
fig.autofmt_xdate()
plt.show()
Here an example code in which I would like to draw the entire date on the x axis
import datetime
import matplotlib.pyplot as plt
import random
import numpy as np
from matplotlib.dates import DateFormatter
years = np.arange( 2005, 2016, 1)
years[1] = 2005
years[2] = 2005
months = [ 3, 2, 1, 5, 7, 12, 2, 3, 5, 2, 6]
dates = []
for Y in years:
dates = dates + [ datetime.datetime(Y, 6 , 4) ]
y = np.random.randn(len(dates))
fig, ax = plt.subplots()
ax.plot_date(dates, y, 'r+', ms = 2, mew = 12)
ax.fmt_xdata = DateFormatter('%y-%m-%d')
fig.autofmt_xdate()
plt.show()
Unfortunately the result is this
And what I need is to write all the date on the x axis like in this picture
What you suggest me to change on my code?
ax.fmt_xdata is the formatter for the dates usually only used for the hover functionality in interactive plots.
What you want here is to set the major formatter of the xaxis to a DateFormatter:
ax.xaxis.set_major_formatter(DateFormatter('%b-%d-%Y'))
I'm trying to plot a graph of dates on the x-axis and values on the y-axis. It works fine, except that I can't get the range of the x-axis to be appropriate. The x-axis range is always Jan 2012 to Jan 2016, despite my dates being from today. I am even specifying that xlim should be the first and last date.
I'm writing this for python-django, if that's relevant.
import datetime
import matplotlib.pyplot as plt
x = [datetime.date(2014, 1, 29), datetime.date(2014, 1, 29), datetime.date(2014, 1, 29)]
y = [2, 4, 1]
fig, ax = plt.subplots()
ax.plot_date(x, y)
ax.set_xlim([x[0], x[-1]])
canvas = FigureCanvas(plt.figure(1))
response = HttpResponse(content_type='image/png')
canvas.print_png(response)
return response
And here is the output:
Edit:
Having seen actual data from the OP, all of the values are at the same date/time. So matplotlib is automatically zooming the x-axis out. You can still manually set the x-axis limits with datetime objects
If I do something like this on matplotlib v1.3.1:
import datetime
import matplotlib.pyplot as plt
x = [datetime.date(2014, 1, 29)] * 3
y = [2, 4, 1]
fig, ax = plt.subplots()
ax.plot_date(x, y, markerfacecolor='CornflowerBlue', markeredgecolor='white')
fig.autofmt_xdate()
ax.set_xlim([datetime.date(2014, 1, 26), datetime.date(2014, 2, 1)])
ax.set_ylim([0, 5])
I get:
And the axes limits match the dates that I specified.
With help from Paul H's solution, I was able to change the range of my time-based x-axis.
Here is a more general solution for other beginners.
import matplotlib.pyplot as plt
import datetime as dt
# Set X range. Using left and right variables makes it easy to change the range.
#
left = dt.date(2020, 3, 15)
right = dt.date(2020, 7, 15)
# Create scatter plot of Positive Cases
#
plt.scatter(
x, y, c="blue", edgecolor="black",
linewidths=1, marker = "o", alpha = 0.8, label="Total Positive Tested"
)
# Format the date into months & days
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m-%d'))
# Change the tick interval
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=30))
# Puts x-axis labels on an angle
plt.gca().xaxis.set_tick_params(rotation = 30)
# Changes x-axis range
plt.gca().set_xbound(left, right)
plt.show()
I got data for several months, but in between some months are missing. This looks quite strange if I plot the whole dataset in one plot (lots of empty space in between).
I wrote the small example script to show how it works (based on: Python/Matplotlib - Is there a way to make a discontinuous axis?)
The problem: I can't get the x-axis use the same date formatting! Either ax or ax2 is correct, but never both of them.
Do you have any idea?
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import datetime
def getDates(startdate, enddate):
days = (enddate + datetime.timedelta(days=1) - startdate).days
dates = [ startdate + datetime.timedelta(days=x) for x in range(0,days) ]
return dates
dates1 = getDates(datetime.datetime(2013,1,1), datetime.datetime(2013,1,31))
dates2 = getDates(datetime.datetime(2013,3,1), datetime.datetime(2013,3,31))
dates = dates1+dates2
data = np.arange(len(dates))
Locator = mpl.dates.DayLocator(interval=5)
Formatter = mpl.dates.DateFormatter('%d-%m-%y')
fig,(ax,ax2) = plt.subplots(1,2,sharey=True)
fig.subplots_adjust(wspace=0.05)
fig.set_size_inches(10,3)
ax.plot(dates, data)
ax2.plot(dates, data)
ax.legend(loc=1)
ax.set_ylim( 0, 61 )
ax.set_xlim( datetime.datetime(2013,1,1), datetime.datetime(2013,1,31) )
ax2.set_xlim( datetime.datetime(2013,3,1), datetime.datetime(2013,3,31) )
labels = ax.get_xticklabels()
for label in labels: label.set_rotation(30)
labels = ax2.get_xticklabels()
for label in labels: label.set_rotation(30)
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.tick_params(right='off')
ax2.tick_params(left='off')
ax2.yaxis.tick_right()
ax.xaxis.set_major_locator(Locator)
ax.xaxis.set_major_formatter(Formatter)
ax2.xaxis.set_major_locator(Locator)
ax2.xaxis.set_major_formatter(Formatter)
plt.savefig("test.png", bbox_inches='tight')
Result:
You have found an interesting detail about the internals of matplotlib. The locator object you pass into set_major_locator is the object used by the axes to figure out where to put it's ticks both axes were using the same locater object. As part of the draw the locator generates a list of where the ticks should be based on the limits of the axes which when it gets done for the second axes means no ticks are visible in the first axes. You just need to pass in distinct (separate instantiations) locator objects, done here with copy.
import datetime
import copy
def getDates(startdate, enddate):
days = (enddate + datetime.timedelta(days=1) - startdate).days
dates = [ startdate + datetime.timedelta(days=x) for x in range(0, days) ]
return dates
dates1 = getDates(datetime.datetime(2013, 1, 1), datetime.datetime(2013, 1, 31))
dates2 = getDates(datetime.datetime(2013, 3, 1), datetime.datetime(2013, 3, 31))
dates = dates1+dates2
data = np.arange(len(dates))
Locator = mpl.dates.DayLocator(interval=5)
Formatter = mpl.dates.DateFormatter('%d-%m-%y')
fig, (ax, ax2) = plt.subplots(1, 2, sharey=True, tight_layout=True)
fig.subplots_adjust(wspace=0.05)
fig.set_size_inches(10, 3, forward=True)
ax.plot(dates, data)
ax2.plot(dates, data)
ax.legend(loc=1)
ax.set_ylim(0, 61)
ax.set_xlim(datetime.datetime(2013, 1, 1), datetime.datetime(2013, 1, 31))
ax2.set_xlim(datetime.datetime(2013, 3, 1), datetime.datetime(2013, 3, 31))
labels = ax.get_xticklabels()
for label in labels:
label.set_rotation(30)
labels = ax2.get_xticklabels()
for label in labels:
label.set_rotation(30)
ax.spines['right'].set_visible(False)
ax2.spines['left'].set_visible(False)
ax.tick_params(right='off')
ax2.tick_params(left='off')
ax2.yaxis.tick_right()
# note the copy here
ax.xaxis.set_major_locator(copy.copy(Locator))
ax.xaxis.set_major_formatter(copy.copy(Formatter))
ax2.xaxis.set_major_locator(copy.copy(Locator))
ax2.xaxis.set_major_formatter(copy.copy(Formatter))