I have the following code to plot a chart with matplotlib
#!/usr/bin/env python
import matplotlib.pyplot as plt
import urllib2
import json
req = urllib2.urlopen("http://localhost:17668/retrieval/data/getData.json? pv=LNLS:ANEL:corrente&donotchunk")
data = json.load(req)
secs = [x['secs'] for x in data[0]['data']]
vals = [x['val'] for x in data[0]['data']]
plt.plot(secs, vals)
plt.show()
The secs arrays is epoch time.
What I want is to plot the data in the x axis (secs) as a date (DD-MM-YYYY HH:MM:SS).
How can I do that?
To plot date-based data in matplotlib you must convert the data to the correct format.
One way is to first convert your data to datetime objects, for an epoch timestamp you should use datetime.datetime.fromtimestamp().
You must then convert the datetime objects to the right format for matplotlib, this can be handled using matplotlib.date.date2num.
Alternatively you can use matplotlib.dates.epoch2num and skip converting your date to datetime objects in the first place (while this will suit your use-case better initially, I would recommend trying to keep date based date in datetime objects as much as you can when working, it will save you a headache in the long run).
Once you have your data in the correct format you can plot it using plot_date.
Finally to format your x-axis as you wish you can use a matplotlib.dates.DateFormatter object to choose how your ticks will look.
import matplotlib.pyplot as plt
import matplotlib.dates as mdate
import numpy as np
# Generate some random data.
N = 40
now = 1398432160
raw = np.array([now + i*1000 for i in range(N)])
vals = np.sin(np.linspace(0,10,N))
# Convert to the correct format for matplotlib.
# mdate.epoch2num converts epoch timestamps to the right format for matplotlib
secs = mdate.epoch2num(raw)
fig, ax = plt.subplots()
# Plot the date using plot_date rather than plot
ax.plot_date(secs, vals)
# Choose your xtick format string
date_fmt = '%d-%m-%y %H:%M:%S'
# Use a DateFormatter to set the data to the correct format.
date_formatter = mdate.DateFormatter(date_fmt)
ax.xaxis.set_major_formatter(date_formatter)
# Sets the tick labels diagonal so they fit easier.
fig.autofmt_xdate()
plt.show()
You can change the ticks locations and formats on your plot:
import matplotlib.pyplot as plt
import matplotlib.ticker as mtick
import time
secs = [10928389,102928123,383827312,1238248395]
vals = [12,8,4,12]
plt.plot(secs,vals)
plt.gcf().autofmt_xdate()
plt.gca().xaxis.set_major_locator(mtick.FixedLocator(secs))
plt.gca().xaxis.set_major_formatter(
mtick.FuncFormatter(lambda pos,_: time.strftime("%d-%m-%Y %H:%M:%S",time.localtime(pos)))
)
plt.tight_layout()
plt.show()
Related
I have two lists representing dates and values respectively:
dates = ['10/6/2020',
'10/7/2020',
'10/8/2020',
'10/9/2020',
'10/12/2020',
'10/13/2020',
'10/14/2020',
'10/15/2020',
'10/16/2020',
'10/19/2020']
and
values = ['40.660',
'39.650',
'41.010',
'41.380',
'39.950',
'40.790',
'41.050',
'40.370',
'40.880',
'40.860']
I want to use seaborn/matplotlib to plot them without using pandas. Is that possible? I've made a few attempts but it doesn't seem to be going too well.
Here's what I've got so far:
def plots(values=values,dates=dates):
sns.lineplot(x=dates,y=sorted(values)[::-1])
sns.scatterplot(x=dates,y=sorted(values)[::-1])
plt.show()
return
crude_data = plots()
But it gives me this:
This is obviously wrong but I don't know how to fix it. The x-axis is also messy, and I'd like to fix that as well and make it more legible without expanding the width of the graph if possible. If that's impossible, then a way to do so while expanding the graph would be happily accepted as well.
Cheers!
Just parse dates from string to datetime.
Otherwise they are treated as strings and lose all their properties besides sortability (which can be wrong, depending on date format).
Also add xticks rotation for better x axis labels.
edit:
Also as others noticed, your numeric data is in string type aswell.
import seaborn as sns
import matplotlib.pyplot as plt
import datetime
dates = ['10/6/2020',
'10/7/2020',
'10/8/2020',
'10/9/2020',
'10/12/2020',
'10/13/2020',
'10/14/2020',
'10/15/2020',
'10/16/2020',
'10/19/2020']
values = ['40.660',
'39.650',
'41.010',
'41.380',
'39.950',
'40.790',
'41.050',
'40.370',
'40.880',
'40.860']
dates = [datetime.datetime.strptime(date, '%m/%d/%Y') for date in dates]
values = [float(val) for val in values]
def plots(values=values,dates=dates):
sns.lineplot(x=dates,y=sorted(values)[::-1])
sns.scatterplot(x=dates,y=sorted(values)[::-1])
plt.xticks(rotation=45)
plt.show()
return crude_data = plots()
You can use datetime library.
import matplotlib.pyplot as plt
from datetime import datetime
import seaborn as sns
dates = ['10/6/2020',
'10/7/2020',
'10/8/2020',
'10/9/2020',
'10/12/2020',
'10/13/2020',
'10/14/2020',
'10/15/2020',
'10/16/2020',
'10/19/2020']
x = [datetime.strptime(i, '%m/%d/%Y') for i in dates]
values = ['40.660',
'39.650',
'41.010',
'41.380',
'39.950',
'40.790',
'41.050',
'40.370',
'40.880',
'40.860']
nvalues = [float(i) for i in values]
def plots(values=nvalues,dates=x):
sns.lineplot(x=dates,y=sorted(values)[::-1])
sns.scatterplot(x=dates,y=sorted(values)[::-1])
plt.xticks(rotation=45, ha='right')
plt.show()
return
import matplotlib.pyplot as plt
from datetime import datetime
crude_data = plots()
Output:
I made 3 changes to your code:
I set the y axis data to be float instead of string by removing the '',
I removed sorted from the two plot lines, and
I added sort=False to the lineplot call
import matplotlib.pyplot as plt
import seaborn as sns
dates = ['10/6/2020',
'10/7/2020',
'10/8/2020',
'10/9/2020',
'10/12/2020',
'10/13/2020',
'10/14/2020',
'10/15/2020',
'10/16/2020',
'10/19/2020']
values = [40.660,
39.650,
41.010,
41.380,
39.950,
40.790,
41.050,
40.370,
40.880,
40.860]
def plots(values=values,dates=dates):
sns.lineplot(x=dates,y=values[::-1], sort=False)
s = sns.scatterplot(x=dates,y=values[::-1])
plt.xticks(rotation=45, ha='right')
plt.show()
return s
crude_data = plots()
This gives:
In the timeline plot I’m making, I want date tickers to show only specified dates. (In my example I show tickers for events ‘A’, but it can be any list on tickers). I found how to do it when x-axis data is numeric (upper subplot in my example), but this won’t work with timestamp date type (bottom plot).
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker
myData = pd.DataFrame({'date':['2019-01-15','2019-02-10','2019-03-20','2019-04-17','2019-05-23','2019-06-11'],'cnt':range(6),'event':['a','b','a','b','a','b']})
myData['date'] = [pd.Timestamp(j) for j in myData['date']]
start = pd.Timestamp('2019-01-01')
stop = pd.Timestamp('2019-07-01')
inxa = myData.loc[myData['event'] == 'a'].index
inxb = myData.loc[myData['event'] == 'b'].index
# create two plots, one with 'cnt' as x-axis, the other 'dates' on x-axis.
fig, ax = plt.subplots(2,1,figsize=(16,9))
ax[0].plot((0,6),(0,0), 'k')
ax[1].plot((start, stop),(0,0))
for g in inxa:
ax[0].plot((myData.loc[g,'cnt'],myData.loc[g,'cnt']),(0,1),c='r')
ax[1].plot((myData.loc[g,'date'],myData.loc[g,'date']),(0,1),c='r')
for g in inxb:
ax[0].plot((myData.loc[g,'cnt'],myData.loc[g,'cnt']),(0,2),c='b')
ax[1].plot((myData.loc[g,'date'],myData.loc[g,'date']),(0,2),c='b')
xlist0 = myData.loc[myData['event']=='a','cnt']
xlist1 = myData.loc[myData['event']=='a','date']
ax[0].xaxis.set_major_locator(ticker.FixedLocator(xlist0))
# ax[1].xaxis.set_major_locator(**???**)
Couldn't find a sufficient duplicate, maybe I didn't look hard enough. There are a number of ways to do this:
Converting to numbers first or using the underlying values of a Pandas DateTime Series
xticks = [mdates.date2num(z) for z in xlist1]
# or
xticks = xlist1.values
and at least a couple ways to use it/them
ax[1].xaxis.set_major_locator(ticker.FixedLocator(xticks))
ax[1].xaxis.set_ticks(xticks)
Date tick labels
How to set the xticklabels for date in matplotlib
how to get ticks every hour?
...
I need to display the time in the x axis using matplotlib, not panda. The y axis should display the temperature. The data has been generated from an api and extracted, before being placed into a multidimensional list. My latest attempt is below. the time is not displayed accurately.
import matplotlib.pyplot as plt
import numpy as np
import urllib.request
import json
from datetime import datetime, timedelta, time
import matplotlib
def graph_data():
weather_api = 'http://api.openweathermap.org/data/2.5/forecast?q=London&id='
source_code = urllib.request.urlopen(weather_api)
content = source_code.read()
codes = json.loads(content.decode("utf8"))
#get the data required and put it in an array called 'weather_data'
weather_data=[]
for each in codes['list']:
weather_data.append([
each['dt_txt'],
each['weather'][0]['description'],
int(each['main']['humidity']),
float(each['wind']['speed']),
int(each['main']['temp_min']),
int(each['main']['temp_max'])])
#store the weather details for each day
day1 = datetime.now()+timedelta(days=1)
forecast_list = []
for each in weather_data:
date=datetime.strptime(each[0], '%Y-%m-%d %H:%M:%S') #convert string to date
if day1.day == date.day:
d=datetime.strptime(each[0],'%Y-%m-%d %H:%M:%S')
# time=d.strftime('%H:%M:%S')
forecast_list.append([d, each[4]])
time=[]
temp=[]
for each in forecast_list:
time.append(each[0])
temp.append(each[1])
print(time)
plt.plot(time, temp, label = 'Temperature')
plt.gcf().autofmt_xdate()
plt.xlabel('Time')
plt.ylabel('Temperature')
plt.title('Temperature for {0}'.format(day1.strftime('%Y-%d-%d')))
plt.legend()
plt.show()
graph_data()
You say "displays part of the year and a random number"—probably it is displaying the time accurately, but as a fractional year. What format do you want it to use?
Try using plt.plot_date instead of plt.plot.
Or, after plotting, you can do
ax = plt.gca()
ax.xaxis_date() # treat the x-axis as dates
ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%H:%M')) # specify the format you want
When I plots the complete data works fine and displays the date on the x-axis:
.
When I zoom into particular portion to view:
the plot shows only the time rather than date, I do understand with less points can't display different set of date but how to show date or set date format even if the graph is zoomed?
dataToPlot = pd.read_csv(fileName, names=['time','1','2','3','4','plotValue','6','7','8','9','10','11','12','13','14','15','16'],
sep=',', index_col=0, parse_dates=True, dayfirst=True)
dataToPlot.drop(dataToPlot.index[0])
startTime = dataToPlot.head(1).index[0]
endTime = dataToPlot.tail(1).index[0]
ax = pd.rolling_mean(dataToPlot_plot[startTime:endTime][['plotValue']],mar).plot(linestyle='-', linewidth=3, markersize=9, color='#FECB00')
Thanks in advance!
I have a solution to make the labels look consistent, though bear in mind that it will also include the time on the "larger scale" time plot.
The code below uses the matplotlib.dates functionality to choose a date format for the x-axis. Note that as we're using the matplotlib formatting you can't simple use df.plot but must instead use plt.plot_date and convert your index to the correct format.
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import dates
# Generate some random data and plot it
time = pd.date_range('07/11/2014', periods=1000, freq='5min')
ts = pd.Series(pd.np.random.randn(len(time)), index=time)
fig, ax = plt.subplots()
ax.plot_date(ts.index.to_pydatetime(), ts.data)
# Create your formatter object and change the xaxis formatting.
date_fmt = '%d/%m/%y %H:%M:%S'
formatter = dates.DateFormatter(date_fmt)
ax.xaxis.set_major_formatter(formatter)
plt.gcf().autofmt_xdate()
plt.show()
An example showing the fully zoomed out plot
An example showing the plot zoomed in.
I have an array of timestamps in the format (HH:MM:SS.mmmmmm) and another array of floating point numbers, each corresponding to a value in the timestamp array.
Can I plot time on the x axis and the numbers on the y-axis using Matplotlib?
I was trying to, but somehow it was only accepting arrays of floats. How can I get it to plot the time? Do I have to modify the format in any way?
Update:
This answer is outdated since matplotlib version 3.5. The plot function now handles datetime data directly. See https://matplotlib.org/3.5.1/api/_as_gen/matplotlib.pyplot.plot_date.html
The use of plot_date is discouraged. This method exists for historic
reasons and may be deprecated in the future.
datetime-like data should directly be plotted using plot.
If you need to plot plain numeric data as Matplotlib date format or
need to set a timezone, call ax.xaxis.axis_date / ax.yaxis.axis_date
before plot. See Axis.axis_date.
Old, outdated answer:
You must first convert your timestamps to Python datetime objects (use datetime.strptime). Then use date2num to convert the dates to matplotlib format.
Plot the dates and values using plot_date:
import matplotlib.pyplot
import matplotlib.dates
from datetime import datetime
x_values = [datetime(2021, 11, 18, 12), datetime(2021, 11, 18, 14), datetime(2021, 11, 18, 16)]
y_values = [1.0, 3.0, 2.0]
dates = matplotlib.dates.date2num(x_values)
matplotlib.pyplot.plot_date(dates, y_values)
You can also plot the timestamp, value pairs using pyplot.plot (after parsing them from their string representation). (Tested with matplotlib versions 1.2.0 and 1.3.1.)
Example:
import datetime
import random
import matplotlib.pyplot as plt
# make up some data
x = [datetime.datetime.now() + datetime.timedelta(hours=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]
# plot
plt.plot(x,y)
# beautify the x-labels
plt.gcf().autofmt_xdate()
plt.show()
Resulting image:
Here's the same as a scatter plot:
import datetime
import random
import matplotlib.pyplot as plt
# make up some data
x = [datetime.datetime.now() + datetime.timedelta(hours=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]
# plot
plt.scatter(x,y)
# beautify the x-labels
plt.gcf().autofmt_xdate()
plt.show()
Produces an image similar to this:
7 years later and this code has helped me.
However, my times still were not showing up correctly.
Using Matplotlib 2.0.0 and I had to add the following bit of code from Editing the date formatting of x-axis tick labels in matplotlib by Paul H.
import matplotlib.dates as mdates
myFmt = mdates.DateFormatter('%d')
ax.xaxis.set_major_formatter(myFmt)
I changed the format to (%H:%M) and the time displayed correctly.
All thanks to the community.
I had trouble with this using matplotlib version: 2.0.2. Running the example from above I got a centered stacked set of bubbles.
I "fixed" the problem by adding another line:
plt.plot([],[])
The entire code snippet becomes:
import datetime
import random
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# make up some data
x = [datetime.datetime.now() + datetime.timedelta(minutes=i) for i in range(12)]
y = [i+random.gauss(0,1) for i,_ in enumerate(x)]
# plot
plt.plot([],[])
plt.scatter(x,y)
# beautify the x-labels
plt.gcf().autofmt_xdate()
myFmt = mdates.DateFormatter('%H:%M')
plt.gca().xaxis.set_major_formatter(myFmt)
plt.show()
plt.close()
This produces an image with the bubbles distributed as desired.
Pandas dataframes haven't been mentioned yet. I wanted to show how these solved my datetime problem. I have datetime to the milisecond 2021-04-01 16:05:37. I am pulling linux/haproxy throughput from /proc so I can really format it however I like. This is nice for feeding data into a live graph animation.
Here's a look at the csv. (Ignore the packets per second column I'm using that in another graph)
head -2 ~/data
date,mbps,pps
2021-04-01 16:05:37,113,9342.00
...
By using print(dataframe.dtype) I can see how the data was read in:
(base) ➜ graphs ./throughput.py
date object
mbps int64
pps float64
dtype: object
Pandas pulls the date string in as "object", which is just type char. Using this as-is in a script:
import matplotlib.pyplot as plt
import pandas as pd
dataframe = pd.read_csv("~/data")
dates = dataframe["date"]
mbps = dataframe["mbps"]
plt.plot(dates, mbps, label="mbps")
plt.title("throughput")
plt.xlabel("time")
plt.ylabel("mbps")
plt.legend()
plt.xticks(rotation=45)
plt.show()
Matplotlib renders all the milisecond time data. I've added plt.xticks(rotation=45) to tilt the dates but it's not what I want. I can convert the date "object" to a datetime64[ns]. Which matplotlib does know how to render.
dataframe["date"] = pd.to_datetime(dataframe["date"])
This time my date is type datetime64[ns]
(base) ➜ graphs ./throughput.py
date datetime64[ns]
mbps int64
pps float64
dtype: object
Same script with 1 line difference.
#!/usr/bin/env python
import matplotlib.pyplot as plt
import pandas as pd
dataframe = pd.read_csv("~/data")
# convert object to datetime64[ns]
dataframe["date"] = pd.to_datetime(dataframe["date"])
dates = dataframe["date"]
mbps = dataframe["mbps"]
plt.plot(dates, mbps, label="mbps")
plt.title("throughput")
plt.xlabel("time")
plt.ylabel("mbps")
plt.legend()
plt.xticks(rotation=45)
plt.show()
This might not have been ideal for your usecase but it might help someone else.