Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Matplotlib - Smooth plot line for x-axis with date values

Im trying to smooth a graph line out but since the x-axis values are dates im having great trouble doing this. Say we have a dataframe as follows

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline

startDate = '2015-05-15'
endDate = '2015-12-5'
index = pd.date_range(startDate, endDate)
data = np.random.normal(0, 1, size=len(index))
cols = ['value']

df = pd.DataFrame(data, index=index, columns=cols)

Then we plot the data

fig, axs = plt.subplots(1,1, figsize=(18,5))
x = df.index
y = df.value
axs.plot(x, y)
fig.show()

we get

enter image description here

Now to smooth this line there are some usefull staekoverflow questions allready like:

  • Generating smooth line graph using matplotlib,
  • Plot smooth line with PyPlot
  • Creating numpy linspace out of datetime

But I just cant seem to get some code working to do this for my example, any suggestions?

like image 278
Runner Bean Avatar asked Nov 12 '16 05:11

Runner Bean


People also ask

How do I create a straight line in matplotlib?

The axhline() function in pyplot module of matplotlib library is used to add a horizontal line across the axis.

How do you smooth out a plot?

Select the plot in the Object Manager. In the Property Manager, select the Line tab. Check the Smooth line check box. Adjust the Smooth tension to obtain the desired smoothing.

What does %Matplotlib do in Python?

Matplotlib is a cross-platform, data visualization and graphical plotting library for Python and its numerical extension NumPy. As such, it offers a viable open source alternative to MATLAB. Developers can also use matplotlib's APIs (Application Programming Interfaces) to embed plots in GUI applications.


2 Answers

You can use interpolation functionality that is shipped with pandas. Because your dataframe has a value for every index already, you can populate it with an index that is more sparse, and fill every previously non-existent indices with NaN values. Then, after choosing one of many interpolation methods available, interpolate and plot your data:

index_hourly = pd.date_range(startDate, endDate, freq='1H')
df_smooth = df.reindex(index=index_hourly).interpolate('cubic')
df_smooth = df_smooth.rename(columns={'value':'smooth'})

df_smooth.plot(ax=axs, alpha=0.7)
df.plot(ax=axs, alpha=0.7)
fig.show()

enter image description here

like image 146
Vlas Sokolov Avatar answered Oct 17 '22 14:10

Vlas Sokolov


There is one workaround, we will create two plots - 1) non smoothed /interploted with date labels 2) smoothed without date labels.

Plot the 1) using argument linestyle=" " and convert the dates to be plotted on x-axis to string type.

Plot the 2) using the argument linestyle="-" and interpolating the x-axis and y-axis using np.linespace and make_interp_spline respectively.

Following is the use of the discussed workaround for your code.

# your initial code
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.interpolate import make_interp_spline
%matplotlib inline
startDate = "2015-05-15"
endDate = "2015-07-5" #reduced the end date so smoothness is clearly seen
index = pd.date_range(startDate, endDate)
data = np.random.normal(0, 1, size=len(index))
cols = ["value"]

df = pd.DataFrame(data, index=index, columns=cols)
fig, axs = plt.subplots(1, 1, figsize=(40, 12))
x = df.index
y = df.value

# workaround by creating linespace for length of your x axis
x_new = np.linspace(0, len(df.index), 300)
a_BSpline = make_interp_spline(
    [i for i in range(0, len(df.index))],
    df.value,
    k=5,
)
y_new = a_BSpline(x_new)

# plot this new plot with linestyle = "-"
axs.plot(
    x_new[:-5], # removing last 5 entries to remove noise, because interpolation outputs large values at the end.
    y_new[:-5],
    "-",
    label="interpolated"
)

# to get the date on x axis we will keep our previous plot but linestyle will be None so it won't be visible
x = list(x.astype(str))
axs.plot(x, y, linestyle=" ", alpha=0.75, label="initial")
xt = [x[i] for i in range(0,len(x),5)]
plt.xticks(xt,rotation="vertical")
plt.legend()
fig.show()

Resulting Plot plot

Overalpped plot to see the smoothing. plot

like image 33
Muktan Patel Avatar answered Oct 17 '22 13:10

Muktan Patel