Python Matplotlib axis is blank for date with trend line - python

I can correctly plot a trendline with price data but the both X Y axis of date formatting is blank. I am not sure what is messing up this plot configuration for the axis. Here is the Python 2.7 code:
y = df['Close']
# calc the trendline http://stackoverflow.com/questions/26447191/how-to-add-trendline-in-python-matplotlib-dot-scatter-graphs
l = []
for t in df['Time']:
datetime_object = datetime.datetime.strptime(str(t), '%H:%M')
print datetime_object.hour
print datetime_object.minute
l.append((3600 * datetime_object.hour + 60 * datetime_object.minute))
x = l
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
fig = plt.figure()
ax = fig.add_subplot(111)
#http://stackoverflow.com/questions/17709823/plotting-timestamps-hour-minute-seconds-with-matplotlib
plt.xticks(rotation=25)
ax = plt.gca()
ax.set_xticks(x)
xfmt = md.DateFormatter('%H:%M')
ax.xaxis.set_major_formatter(xfmt)
ax.plot(x, p(x), 'r--')
ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%3.4f')) #http://stackoverflow.com/questions/29188757/matplotlib-specify-format-of-floats-for-tick-lables
plt.show()
Also, df['Close'] would have value samples of:
114.684
114.679
df['Time'] would contains sample values:
23:20
23:21

Update: I found the source of your problem.
In addition to the below problem you incorrectly copied the answer to the linked question.
You wrote: ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%3.4f'))
You need: ax.yaxis.set_major_formatter(FormatStrFormatter('%3.4f'))
See updated graph:
https://imgur.com/a/RvO4z
In your code, you begin axis changes before you have actually plotted anything.
If you move your ax.plot(x, p(x), 'r--') to just below your add_subplot line this will work:
import numpy as np
from matplotlib import pyplot as plt
import datetime
import matplotlib
from matplotlib.ticker import FormatStrFormatter
df = pandas.DataFrame()
df['Time'] = pandas.Series(['23:2','22:1'])
df['Close'] = pandas.Series([114.,114.])
y = df['Close']
# calc the trendline http://stackoverflow.com/questions/26447191/how-to-add-trendline-in-python-matplotlib-dot-scatter-graphs
l = []
for t in df['Time']:
datetime_object = datetime.datetime.strptime(str(t), '%H:%M')
print datetime_object.hour
print datetime_object.minute
l.append((3600 * datetime_object.hour + 60 * datetime_object.minute))
x = l
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
fig = plt.figure()
ax = fig.add_subplot(111)
#Added:
ax.plot(x, p(x), 'r--')
#http://stackoverflow.com/questions/17709823/plotting-timestamps-hour- minute-seconds-with-matplotlib
plt.xticks(rotation=25)
ax = plt.gca()
ax.set_xticks(x)
xfmt = md.DateFormatter('%H:%M')
ax.xaxis.set_major_formatter(xfmt)
# REMOVED: ax.plot(x, p(x), 'r--')
# Changed: ax.yaxis.set_major_formatter(mtick.FormatStrFormatter('%3.4f'))
ax.yaxis.set_major_formatter(FormatStrFormatter('%3.4f'))
#http://stackoverflow.com/questions/29188757/matplotlib-specify-format-of- floats-for-tick-lables
plt.show()

Related

Python matplotlib.animation Jupyter Notebook

I use Windows 10 / 64 / Google chrome
I found a good set-up for animation over Jupyter with the call %matplotlib notebook as here :
import numpy as np
import scipy.stats as st
%matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
For exemple, this one is working pretty well :
n = 100
X = st.norm(0,1).rvs(200)
number_of_frames = np.size(X)
def update_hist(num, second_argument):
plt.cla()
plt.hist(X[:num], bins = 20)
plt.title("{}".format(num))
plt.legend()
fig = plt.figure()
hist = plt.hist(X)
ani = animation.FuncAnimation(fig, update_hist, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
But, weirdly the code below doesn't work while it's the same structure, it puzzles me :
X = np.linspace(-5,5, 150)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n, second_argument):
#plt.cla()
plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
plt.plot(X, [fen(sample_sort[:n],h[n],x) for x in X], label = "densité")
plt.title("n = {}".format(n))
fig = plt.figure(6)
plot = plt.plot(X, [f(x) for x in X], c = "y", label = "densité")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, fargs=(X, ), repeat = False )
plt.show()
Thanks for your help, best regards.
EDIT : You don't have the funciton fen(sample_sort[:n],h[n],x) it is a function from float to float taking a x in argument and returning a flot. The argument sample_sort[:n],h[n] it is just maths things I'm trying to understand some statistics anyway, you can remplace with line with what you want np.cos(N[:n]) for exemple.
EDIT : New code according to the suggestion :
N_max = 100
X = np.linspace(-5,5, N_max )
number_of_frames = np.size(X)
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X, np.array([fen(sample_sort[:n],h[n],x) for x in X]))
ax.set_title("n = {}".format(n))
return lines
fig = plt.figure()
ax = plt.axes(xlim=(-4, 4), ylim=(-0.01, 1))
ax.plot(X, np.array([f(x) for x in X]), 'y-', lw=2, label="d")
lines, = ax.plot([], [], 'b--', lw=3, label="f")
ani = animation.FuncAnimation(fig, update_plot, number_of_frames, repeat = False )
plt.show()
EDIT 2:
I found a code over internet which does exactly what I would like
# Fermi-Dirac Distribution
def fermi(E: float, E_f: float, T: float) -> float:
return 1/(np.exp((E - E_f)/(k_b * T)) + 1)
# Create figure and add axes
fig = plt.figure(figsize=(6, 4))
ax = fig.add_subplot(111)
# Get colors from coolwarm colormap
colors = plt.get_cmap('coolwarm', 10)
# Temperature values
T = np.array([100*i for i in range(1,11)])
# Create variable reference to plot
f_d, = ax.plot([], [], linewidth=2.5)
# Add text annotation and create variable reference
temp = ax.text(1, 1, '', ha='right', va='top', fontsize=24)
# Set axes labels
ax.set_xlabel('Energy (eV)')
ax.set_ylabel('Fraction')
# Animation function
def animate(i):
x = np.linspace(0, 1, 100)
y = fermi(x, 0.5, T[i])
f_d.set_data(x, y)
f_d.set_color(colors(i))
temp.set_text(str(int(T[i])) + ' K')
temp.set_color(colors(i))
# Create animation
ani = animation.FuncAnimation(fig, animate, frames=range(len(T)), interval=500, repeat=False)
# Ensure the entire plot is visible
fig.tight_layout()
# show animation
plt.show()
What I want to draw is a curve at random because the actual state of the function is unknown. The basic structure looks like this, so please modify it based on this.
import numpy as np
import scipy.stats as st
# %matplotlib notebook
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# from IPython.display import HTML
# from matplotlib.animation import PillowWriter
X = np.linspace(-5,5, 100)
number_of_frames = np.size(X)
N_max = 100
N = np.arange(1,N_max+1)
h = 1/np.sqrt(N)
def update_plot(n):
#plt.cla()
lines.set_data(X[:n], h[:n])
lines2.set_data(X[:n], h[:n]*-1)
ax.set_title("n = {}".format(n))
return lines, lines2
fig = plt.figure()
ax = plt.axes(xlim=(-5, 5), ylim=(-1, 1))
lines, = ax.plot([], [], 'y-', lw=2, label="densité")
lines2, = ax.plot([], [], 'b--', lw=3, label="densité2")
ani = animation.FuncAnimation(fig, update_plot, frames=number_of_frames, repeat=False )
plt.show()
# ani.save('lines_ani2.gif', writer='pillow')
# plt.close()
# HTML(ani.to_html5_video())

How can I animate Pandas dataframe using matplotlib

I have a dataframe that I want to animate (line chart) using matplotlib. My x and y values:
here x = df.index and y = df['Likes']
x y
0 200000
1 50000
2 1000000
.so on.. ....
Code I tried:
from matplotlib import pyplot as plt
from matplotlib import animation
import pandas as pd
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
fig = plt.figure()
ax = plt.axes(xlim=(0, 18), ylim=(6514, 209124))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
line.set_data(df.index[i], df['Likes'][i])
return line,
anim = animation.FuncAnimation(fig, animate, frames=len(df['Likes']), init_func=init, interval=300, blit=True)
plt.show()
I have tried this, but it is showing blank output with no error message. I am using python 3.83, windows machine. Can I do this using numpy? Almost all of the examples used numpy data in FuncAnimation.
I have solved it myself, I have used code of "vkakerbeck" from github as a guide to add more data points:
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
df = pd.read_csv("C:\\Users\\usr\\Documents\\Sublime\\return_to_windows\\Files\\cod2019.txt", sep='\t')
dg = df['Likes']
x_data = []
y_data = []
fig, ax = plt.subplots()
ax.set_xlim(0, len(dg))
ax.set_ylim(0, dg.max() * 1.04) # multiplied with 1.04 to add some gap in y-axis
line, = ax.plot(0, 0)
This part is for formatting
ax.set_xlabel('Part No')
ax.set_ylabel('Number of Likes')
ax.set_title('Likes in Call of Duty 2019')
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
fig = plt.gcf()
fig.set_size_inches(12.8, 7.2) # 720p output
I have used this from that guide to add more data points to make the animation less jumpy:
x = np.array(dg.index)
y = np.array(dg)
def augment(xold, yold, numsteps):
xnew = []
ynew = []
for i in range(len(xold) - 1):
difX = xold[i + 1] - xold[i]
stepsX = difX / numsteps
difY = yold[i + 1] - yold[i]
stepsY = difY / numsteps
for s in range(numsteps):
xnew = np.append(xnew, xold[i] + s * stepsX)
ynew = np.append(ynew, yold[i] + s * stepsY)
return xnew, ynew
XN, YN = augment(x, y, 3)
augmented = pd.DataFrame(YN, XN)
ylikes = augmented[0].reset_index() # Index reset to avoid key error
Main Function:
def animation_frame(i):
x_data.append(augmented.index[i])
y_data.append(ylikes[0][i])
line.set_xdata(x_data)
line.set_ydata(y_data)
return line,
plt.cla()
plt.tight_layout()
anima = animation.FuncAnimation(fig, func=animation_frame, frames=len(augmented), interval=80)
plt.show()
Export as mp4
Writer = animation.writers['ffmpeg']
writer = Writer(fps=15, bitrate=1000)
anima.save('lines3.mp4', writer=writer)

Plotting values of the bar above it on the graph. Can someone guide me through how to do this? Thanks

from matplotlib import pyplot as plt
import pandas as pd
df = pd.DataFrame(ItemTrack, columns=['Items Taken'])
df.insert(1, "Expenditure", ExpenseTrack, True)
df['Items Taken'] = df['Items Taken'].str.capitalize()
print(df)
x = df['Items Taken']
y = df['Expenditure']
plt.bar(x, y)
plt.xticks(rotation=30, color='green')
plt.yticks(rotation=30, color='red')
plt.show()
Here you go:
from matplotlib import pyplot as plt
import pandas as pd
df = pd.DataFrame({
'Items Taken': [f'Item {i}' for i in range(5)],
'Expenditure' : [i * 100 for i in range(5)]
})
df['Items Taken'] = df['Items Taken'].str.capitalize()
x = df['Items Taken']
y = df['Expenditure']
plt.bar(x, y)
plt.xticks(rotation=30, color='green')
plt.yticks(rotation=30, color='red')
for i, v in enumerate(y):
plt.text(i, v, str(v), color='red', ha='center', fontweight='bold')
plt.show()
Try using ax.text() and loop over the coordinates.
Set the transform keyword argument to ax.transData so the that the coordinates you pass correspond with the datarange in you axis. alternatively you can use ax.transAxes or fig.transFigure.
fig = plt.figure()
ax = plt.axes()
ax.bar(x, y)
# assuming that the you want the y value above the bar
offset_y = 2 # offset to place the text above the bar. Chosen number here is arbitrary
for i range(x):
ax.text(i, y[i]+offset_y, y[i],
va='center',
ha='center',
transform=ax.transData
)
ax.set_xticks(rotation=30, color='green')
ax.set_yticks(rotation=30, color='red')
plt.show()

How to create equal number of primary and secondary y-axes ticks with matplotlib?

I have been working for a while to create a plot with secondary axis so that both the primary and secondary axes have equal number of major ticks so that the grid lines coincide. In the figure below I have shown grid lines on the secondary axis to illustrate the problem.
By manually setting the secondary axis limits I got this plot, which is my desired output:
I have included the reproducible code:
import numpy as np
import matplotlib.pyplot as plt
data = np.loadtxt('data.dat', skiprows=2, delimiter=',', unpack=True).transpose()
time = data[:,0]
pressure = data[:,1]
lift = data[:,2]
figure_pressure_trace = plt.figure(figsize=(5.15, 5.15))
figure_pressure_trace.clf()
P_vs_t = plt.subplot(111)
P_vs_t.plot(time, pressure, linewidth=1.0)
P_vs_t.set_ylabel(r'\textit{Pressure (bar)}', labelpad=6)
P_vs_t.set_xlabel(r'\textit{Time (ms)}', labelpad=6)
lift_vs_t = P_vs_t.twinx()
lift_vs_t.plot(time, lift, color='#4DAF4A')
lift_vs_t.set_ylabel(r'\textit{Lift(mm)}', labelpad=6)
plt.show()
plt.close()
The data is available here.
UPDATE:
I created a function to create equal number of ticks, the entire code is:
import numpy as np
import matplotlib.pyplot as plt
def equal_y_ticks(primary, secondary):
y_min_primary, y_max_primary = primary.get_ybound()
y_min_secondary, y_max_secondary = secondary.get_ybound()
primary_ticks = len(primary.yaxis.get_major_ticks())
secondary_ticks = len(secondary.yaxis.get_major_ticks())
primary_spacing = (y_max_primary - y_min_primary) / (primary_ticks - 1)
secondary_spacing = (y_max_secondary - y_min_secondary) / (secondary_ticks - 1)
ticks = max(primary_ticks, secondary_ticks)
if secondary_ticks < primary_ticks:
y_max_secondary = y_min_secondary + (primary_ticks * secondary_spacing)
secondary.yaxis.set_ticks(np.arange(y_min_secondary, y_max_secondary, secondary_spacing))
else:
y_max_primary = y_min_primary + (secondary_ticks * primary_spacing)
primary.yaxis.set_ticks(np.arange(y_min_primary, y_max_primary, primary_spacing))
data = np.loadtxt('data.dat', skiprows=2, delimiter=',', unpack=True).transpose()
time = data[:,0]
pressure = data[:,1]
lift = data[:,2]
figure_pressure_trace = plt.figure(figsize=(5.15, 5.15))
figure_pressure_trace.clf()
P_vs_t = plt.subplot(111)
P_vs_t.plot(time, pressure, linewidth=1.0)
P_vs_t.set_ylabel(r'\textit{Pressure (bar)}', labelpad=6)
P_vs_t.set_xlabel(r'\textit{Time (ms)}', labelpad=6)
lift_vs_t = P_vs_t.twinx()
lift_vs_t.plot(time, lift, color='#4DAF4A')
equal_y_ticks(P_vs_t, lift_vs_t)
lift_vs_t.set_ylabel(r'\textit{Lift(mm)}', labelpad=6)
plt.show()
plt.close()
But this function gives me plots like these (for some data):
I think you are looking for LinearLocator (docs)
import matplotlib.pyplot as plt
from matplotlib import ticker as mtick
fig, ax = plt.subplots()
ax2 = ax.twinx()
ax.yaxis.set_major_locator(mtick.LinearLocator(5))
ax2.yaxis.set_major_locator(mtick.LinearLocator(5))
ax.set_ylim(0, 15)
ax2.set_ylim(0, 1500)
ax.yaxis.grid(True, lw=7, color='g', ls='--')
ax2.yaxis.grid(True, color='k', ls='-', lw=3)
Which will put N evenly spaced ticks between the min and max.

If function for annotating from pandas

I'm scatter plotting values from pandas dataframe. I would like to annotate points only if the value is greater than 100. I have no idea how to go about it.
Here's the code I'm working with (it's terrible but I'm very new to this):
female_data = r'/home/jg/Desktop/hurricanedata_f.csv'
female_df = read_csv(female_data)
male_data = r'/home/jg/Desktop/hurricanedata_m.csv'
male_df = read_csv(male_data)
x = female_df['Year']
y = female_df['alldeaths']
z = female_df['Name']
y_mean = [np.mean(y) for i in x]
a = male_df['Year']
b = male_df['alldeaths']
b_mean = [np.mean(b) for i in b]
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.annotate('Agnes', xy=(1972,117))
ax1 = fig.add_subplot(1,1,1)
ax1.scatter(x,y, label = 'female', color = 'r')
ax2 = fig.add_subplot(1,1,1)
ax2.scatter(a,b, label = 'male')
ax3 = fig.add_subplot(1,1,1)
ax3.plot(x, y_mean, linestyle='--', color = 'r')
ax4 = fig.add_subplot(1,1,1)
ax4.plot(a, b_mean, linestyle='--', color = 'blue')
plt.title('Hurricanes')
plt.xlabel('Year')
plt.ylabel('Deaths')
plt.legend(loc='upper right')
plt.ylim([-5,300])
plt.xlim([1948,2020])
plt.show()
You can loop over all your data points and check if each is greater than 100. Then give those points an annotation.
import matplotlib.pyplot as plt
import numpy as np
import string
# Fake data
x = np.arange(10)
y = 10*np.random.rand(10) + 95
names = string.lowercase[:10] # first 10 lowercase letters
# Plot data
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.scatter(x,y)
# Annonate points with y values greater than 100
for xi, yi, iname in zip(x,y,names): # Loop over x and y values
if yi > 100: # Check if y is greater than 100
ax.annotate(iname, (xi, yi),size = 30) # Add an annoatation.
plt.show()

Categories