add a subplot to the plot produced by a previous function - python

I've written a function that reads data from a csv file and plots it. Now I need to add a subplot with another part of the data from the same file, so I've tried to write a function that calls the first function and adds a subplot. When I do this, I get the two to show up as different figures. How can I suppress this and make both of them show in the same figure?
Here is a mockup of my code:
def timex(h_ratio = [3, 1]):
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.gridspec as gridspec
total_height = h_ratio[0] + h_ratio[1]
gs = gridspec.GridSpec(total_height, 1)
time = [1, 2, 3, 4, 5]
x = [1, 2, 3, 4, 5]
y = [1, 1, 1, 1, 1]
ax1 = plt.subplot(gs[:h_ratio[0], :])
plt.plot(time, x)
plot = plt.gcf
plt.show()
return time, x, y, plot, gs, h_ratio
def timeyx():
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
time, x, y, plot, gs, h_ratio = timex(h_ratio = [3, 1])
ax2 = plt.subplot(gs[h_ratio[1], :])
plt.plot(time, y)
plt.show()
timeyx()
I realize that I have two plt.show() statements, but if I remove one that figure will not show at all.

I am not sure whether you need to use matplotlib.gridspec specifically or not, but you can use subplot2grid to make the job easy.
import matplotlib.pyplot as plt
def timex():
time = [1, 2, 3, 4, 5]
x = [1, 2, 3, 4, 5]
y = [1, 1, 1, 1, 1]
ax1 = plt.subplot2grid((1,2), (0,0))
ax1.plot(time, x)
return time, x, y
def timeyx():
time, x, y = timex()
ax2 = plt.subplot2grid((1,2), (0,1))
ax2.plot(time, y)
timeyx()
plt.show()
This produces one figure shown below with two subplots:

Related

how to animate from pandas x y coordinate timeseries

say I have:
df ={'animal' : [1, 1, 1, 2, 2, 3, 3, 3, 3],
'x':[76.551, 77.529, 78.336,79.249, 76.077, 77, 78.02, 79.23, 77.733],
'y': [151.933, 152.945, 153.970, 152.004, 153.027, 119.369, 120.615, 118.935, 119.115],
'time': [0, 1, 2, 0, 1, 0, 3,2,5]}
df = pd.DataFrame(df)
how can I animate the trajectory of each animal (in different colours) according to the time and keep the previous trajectories on the graph (and ideally if there are overlapping trajectories, the latest one shows in front)?
This plots just the first animal:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
%matplotlib notebook #to enable animation in jupyter notebook
fig = plt.figure()
ax = plt.axes(xlim=(75, 80), ylim=(119, 155))
line, = ax.plot([], [], lw=2)
# initialization function
def init():
# creating an empty plot/frame
line.set_data([], [])
return line,
# lists to store x and y axis points
xdata, ydata = [], []
# animation function
def animate(i):
for animal, subdf in df.sort_values('time').groupby('animal'):
xdata.append(subdf['x'])
ydata.append(subdf['y'])
line.set_data(xdata, ydata)
return line,
# setting a title for the plot
plt.title('')
# hiding the axis details
#plt.axis('off')
# call the animator
anim = animation.FuncAnimation(fig, animate, init_func=init, interval=100)
# save the animation as mp4 video file
#anim.save('test.gif',writer='imagemagick')
Also, i'm unable to slow down the animation by adjusting interval, all it does is increase the delay from when the animation starts, but doesn't slow the speed of the animation itself.
EDIT: i realize in the animation() function i isn't actually passed through anything... but if i replace it with df nothing plots at all. i'm pretty unfamiliar with this package
I don't understand what you want to get in this cycle:
"for animal, subdf in df.sort_values('time').groupby('animal'):"
And the values in this loop always remain the same, so there is no movement (animation). As far as I understand you, you need a trajectory of movement along three points, each time different. I made a random selection of three elements from df['x'] and df[y'] at each iteration. Look is this what you wanted to see?
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
df ={'animal' : [1, 1, 1, 2, 2, 3, 3, 3, 3],
'x':[76.551, 77.529, 78.336,79.249, 76.077, 77, 78.02, 79.23, 77.733],
'y': [151.933, 152.945, 153.970, 152.004, 153.027, 119.369, 120.615, 118.935, 119.115],
'time': [0, 1, 2, 0, 1, 0, 3,2,5]}
df = pd.DataFrame(df)
x = df['x'].sample(n=3)
y = df['y'].sample(n=3)
fig = plt.figure()
ax = plt.axes(xlim=(75, 80), ylim=(119, 155))
ax.plot(x, y, marker='o')
# initialization function
def init():
pass
# animation function
def animate(i):
x = df['x'].sample(n=3)
y = df['y'].sample(n=3)
ax.plot(x, y, marker='o')
# setting a title for the plot
plt.title('Animal animation')
anim = animation.FuncAnimation(fig, animate, init_func=init, interval=1000)
plt.show()

Plotting time series data with with 30sec break point and color

I am new in python programming. I can simply plot the input data shown in the figure with my code but how can I plot the time series data as mention in the figure. Any code and suggestions will be thankful.
My code is:
import matplotlib.pyplot as plt
import numpy as np
y_values = [5, 5, 1, 1, 5, 5, 1, 1, 5, 1, 1]
x_values = np.arange(30, 331, 30)
plt.figure()
plt.plot(x_values,y_values,"-x")
plt.show()
Although there is a way to draw a series of rectangular shapes, we used a general method and used horizontal bar charts. We added a list for the values in the bar chart and stacked the values. Class label names and class titles are now supported as annotations. You can try various other parameters.
import matplotlib.pyplot as plt
import numpy as np
y = [5]*11
y_values = [5, 5, 1, 1, 5, 5, 1, 1, 5, 1, 1]
x_values = np.arange(30, 331, 30)
fig, ax = plt.subplots(figsize=(12,1))
ax.barh(y=0, height=1.0, edgecolor='k', width=y[0], label='Time Interval')
for i in range(len(y)):
if y_values[i] == 5:
color = 'y'
else:
color = 'm'
ax.barh(y=0, left=sum(y[:i]), height=1.0, width=y[i], color=color, edgecolor='k', label='Time Interval')
for s in ['top','bottom','left','right']:
ax.spines[s].set_visible(False)
for i,(p,t) in enumerate(zip(y, y_values)):
ax.text(y=0.6, x=2.5+p*i, s=str(t))
ax.text(-0.08, 1, 'Class', transform=ax.transAxes)
ax.set_xticks([])
ax.set_yticks([])
ax.set_ylabel('Time Interval', rotation=0, labelpad=40, loc='center')
plt.show()
Try:
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
y_values = ['class', 5, 5, 1, 1, 5, 5, 1, 1, 5, 1, 1]
x_values = np.arange(30, 331, 30)
x_values = np.concatenate((['Time'],x_values))
df = pd.DataFrame(data={'class': y_values, 'Time': x_values})
colors = {5: 'gold', 1: 'darkviolet'}
df['colors'] = df['class'].map(colors)
df['colors'].fillna('white', inplace=True)
df['Time'].iloc[1:] = ''
print(df)
fig, ax =plt.subplots(1,1)
ax.axis('tight')
ax.axis('off')
data = df.T.values
colors = [data[2].tolist()]
table = ax.table(cellText=[data[1].tolist()], colLabels=data[0].tolist(),loc="center", cellColours=colors)
table.set_fontsize(14)
for i in range(len(data[0])):
table[0, i].visible_edges = ''
table[1, 0].visible_edges = ''
table.scale(1.5, 1.5)
plt.show()

Update text in matplotlib's plot

I want to change text in matplotlib's plot with loop. I am able to print text with loop, but unable to delete the previous text and they got printed on top of each other.
import numpy as np
import matplotlib.pyplot as plt
x = np.array([1,2,3,4,5])
y = np.array([1,2,3,4,5])
fig, ax = plt.subplots()
ax.set_xlim([0,5])
ax.set_ylim([0,5])
for i in x:
pt = ax.plot(i, i, 'o')
tx = ax.text(1, 2, str(i), fontsize = 12)
plt.pause(1)
removePt = pt.pop()
removePt.remove()
I tried to delete text by
removeTx = tx.pop()
removeTx.remove()
but it has not worked.
Kindly suggest how can I remove the previous text from plot.
Just add tx.remove() after the pause:
import numpy as np
import matplotlib.pyplot as plt
x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 2, 3, 4, 5])
fig, ax = plt.subplots()
ax.set_xlim([0, 5])
ax.set_ylim([0, 5])
for i in x:
pt = ax.plot(i, i, 'o')
tx = ax.text(1, 2, str(i), fontsize = 12)
plt.pause(1)
tx.remove()
plt.show()

How to plot a scatter plot over a line plot? [duplicate]

Does anybody have a suggestion on what's the best way to present overlapping lines on a plot? I have a lot of them, and I had the idea of having full lines of different colors where they don't overlap, and having dashed lines where they do overlap so that all colors are visible and overlapping colors are seen.
But still, how do I that.
I have the same issue on a plot with a high degree of discretization.
Here the starting situation:
import matplotlib.pyplot as plt
grid=[x for x in range(10)]
graphs=[
[1,1,1,4,4,4,3,5,6,0],
[1,1,1,5,5,5,3,5,6,0],
[1,1,1,0,0,3,3,2,4,0],
[1,2,4,4,3,2,3,2,4,0],
[1,2,3,3,4,4,3,2,6,0],
[1,1,3,3,0,3,3,5,4,3],
]
for gg,graph in enumerate(graphs):
plt.plot(grid,graph,label='g'+str(gg))
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()
No one can say where the green and blue lines run exactly
and my "solution"
import matplotlib.pyplot as plt
grid=[x for x in range(10)]
graphs=[
[1,1,1,4,4,4,3,5,6,0],
[1,1,1,5,5,5,3,5,6,0],
[1,1,1,0,0,3,3,2,4,0],
[1,2,4,4,3,2,3,2,4,0],
[1,2,3,3,4,4,3,2,6,0],
[1,1,3,3,0,3,3,5,4,3],
]
for gg,graph in enumerate(graphs):
lw=10-8*gg/len(graphs)
ls=['-','--','-.',':'][gg%4]
plt.plot(grid,graph,label='g'+str(gg), linestyle=ls, linewidth=lw)
plt.legend(loc=3,bbox_to_anchor=(1,0))
plt.show()
I am grateful for suggestions on improvement!
Just decrease the opacity of the lines so that they are see-through. You can achieve that using the alpha variable. Example:
plt.plot(x, y, alpha=0.7)
Where alpha ranging from 0-1, with 0 being invisible.
imagine your panda data frame is called respone_times, then you can use alpha to set different opacity for your graphs. Check the picture before and after using alpha.
plt.figure(figsize=(15, 7))
plt.plot(respone_times,alpha=0.5)
plt.title('a sample title')
plt.grid(True)
plt.show()
Depending on your data and use case, it might be OK to add a bit of random jitter to artificially separate the lines.
from numpy.random import default_rng
import pandas as pd
rng = default_rng()
def jitter_df(df: pd.DataFrame, std_ratio: float) -> pd.DataFrame:
"""
Add jitter to a DataFrame.
Adds normal distributed jitter with mean 0 to each of the
DataFrame's columns. The jitter's std is the column's std times
`std_ratio`.
Returns the jittered DataFrame.
"""
std = df.std().values * std_ratio
jitter = pd.DataFrame(
std * rng.standard_normal(df.shape),
index=df.index,
columns=df.columns,
)
return df + jitter
Here's a plot of the original data from Markus Dutschke's example:
And here's the jittered version, with std_ratio set to 0.1:
Replacing solid lines by dots or dashes works too
g = sns.FacetGrid(data, col='config', row='outputs', sharex=False)
g.map_dataframe(sns.lineplot, x='lag',y='correlation',hue='card', linestyle='dotted')
Instead of random jitter, the lines can be offset just a little bit, creating a layered appearance:
import matplotlib.pyplot as plt
from matplotlib.transforms import offset_copy
grid = list(range(10))
graphs = [[1, 1, 1, 4, 4, 4, 3, 5, 6, 0],
[1, 1, 1, 5, 5, 5, 3, 5, 6, 0],
[1, 1, 1, 0, 0, 3, 3, 2, 4, 0],
[1, 2, 4, 4, 3, 2, 3, 2, 4, 0],
[1, 2, 3, 3, 4, 4, 3, 2, 6, 0],
[1, 1, 3, 3, 0, 3, 3, 5, 4, 3]]
fig, ax = plt.subplots()
lw = 1
for gg, graph in enumerate(graphs):
trans_offset = offset_copy(ax.transData, fig=fig, x=lw * gg, y=lw * gg, units='dots')
ax.plot(grid, graph, lw=lw, transform=trans_offset, label='g' + str(gg))
ax.legend(loc='upper left', bbox_to_anchor=(1.01, 1.01))
# manually set the axes limits, because the transform doesn't set them automatically
ax.set_xlim(grid[0] - .5, grid[-1] + .5)
ax.set_ylim(min([min(g) for g in graphs]) - .5, max([max(g) for g in graphs]) + .5)
plt.tight_layout()
plt.show()

How can I use Matplotlib to re-adjust limits of an axis (added to host plot) using the interactive zoom tool?

I am displaying information with two y-axes and a common x-axis using the following script.
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as AA
#creating a host plot with x and y axis
hostplot = host_subplot(111, axes_class=AA.Axes)
#creating a second y axis
extra_y_axis = hostplot.twinx()
extra_y_axis.set_navigate_mode(True)
extra_y_axis.set_navigate(True)
print extra_y_axis.can_zoom() #prints true on output
hostplot.set_xlabel("host_x")
hostplot.set_ylabel("host_y")
extra_y_axis.set_ylabel("extra_y")
hostplot.plot([0, 1, 2], [0, 1, 2])
extra_y_axis.plot([0, 1, 2], [0, 3, 2])
plt.draw()
plt.show()
After this I used the 'Zoom to Rectangle' tool from the tray in the bottom-left as shown below:
.
And I got the following output:
.
Please notice the y-axis scales in both the images. While the zoom functionality is working correctly for the host plot, I am unable to get the extra_y_axis to rescale and it just maintains a constant scale throughout (so I can't really zoom in on plots using the second axis).
How can I make it so that all the axes are rescaled on zooming in a small portion?
Thanks
I've traced down your problem to the sue of the axes_grid1 toolkit. If you don't require the use of this toolkit you can easily fix your issue by initialising your figure in the usual manner:
import matplotlib.pyplot as plt
#creating a host plot with x and y axis
fig, hostplot = plt.subplots()
#creating a second y axis
extra_y_axis = hostplot.twinx()
hostplot.set_xlabel("host_x")
hostplot.set_ylabel("host_y")
extra_y_axis.set_ylabel("extra_y")
hostplot.plot([0, 1, 2], [0, 1, 2])
extra_y_axis.plot([0, 1, 2], [0, 3, 2])
plt.show()
If you do want to use the toolkit then you have to add a couple of lines to get the two y axes to scale and transform together:
import matplotlib.transforms as mtransforms
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.parasite_axes import SubplotHost
fig = plt.figure()
ax1 = SubplotHost(fig, 1, 1, 1)
#set the scale difference between the two y axes
aux_trans = mtransforms.Affine2D().scale(sx = 1.,sy= 1.5)
ax2 = ax1.twin(aux_trans)
fig.add_subplot(ax1)
ax1.plot([0, 1, 2], [0, 1, 2])
ax2.plot([0, 1, 2], [0, 3, 2])
ax1.set_ylim(0,3)
plt.show()

Categories

Resources