So I am trying to plot accelerometer data with regards to time, my csv reads like this(columns -> time, x, y, z):
1518999378635,2.275090217590332,8.601768493652344,3.691260576248169
1518999378653,2.38462495803833,8.633491516113281,4.0964789390563965
1518999378658,2.449866771697998,8.506000518798828,4.082113742828369
1518999378667,2.4372973442077637,8.166622161865234,4.016273498535156
1518999378675,1.8381483554840088,8.848969459533691,4.086902141571045
1518999378681,1.1402385234832764,8.762179374694824,4.225766181945801
1518999378688,1.7818846702575684,8.652046203613281,3.6110546588897705
1518999378694,2.076371431350708,8.80467700958252,4.0527849197387695
1518999378700,2.3720552921295166,8.471882820129395,4.120420932769775
My initial bet (as given below!) was to use a scatter with time as color, however the output is, well, not very obvious.
from numpy import genfromtxt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
if __name__ == "__main__":
print("Plotting Accelerometer Data")
acm_data = genfromtxt("acm_data.csv", delimiter=',', names="time, acc_x, acc_y, acc_z")
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = acm_data["acc_x"]
y = acm_data["acc_y"]
z = acm_data["acc_z"]
c = acm_data["time"]
ax.scatter(x, y, z, c=c, cmap=plt.hot())
plt.show()
The output looks viz:
and is not very interpretable. What would be the best way to handle this?
Thanks.
Something like this:
import matplotlib.pyplot as plt
x = [0, 1, 2, 3]
x_accel = [5, 6, 3, 4]
y_accel = [2, 7, 6, 8]
z_accel = [1, 2, 3, 4]
plt.subplot(3, 1, 1)
plt.plot(x, x_accel, '.-')
plt.title('A tale of 3 subplots')
plt.ylabel('X acceleration')
plt.subplot(3, 1, 2)
plt.plot(x, y_accel, '.-')
plt.xlabel('time (s)')
plt.ylabel('Y acceleration')
plt.subplot(3, 1, 3)
plt.plot(x, z_accel, '.-')
plt.xlabel('time (s)')
plt.ylabel('Z acceleration')
plt.show()
Generates:
Of course you'll have to mess with your axes and what not to make the presentation of your data as clear as possible. But in general, this is much clearer than what is posted in your question.
Well, here's my answer (break it into 3 2-dimensional plots):
from numpy import genfromtxt
import matplotlib.pyplot as plt
import numpy as np
if __name__ == "__main__":
print("Plotting Accelerometer Data")
acm_data = genfromtxt("acm_data.csv", delimiter=',', names="time, acc_x, acc_y, acc_z")
fig = plt.figure()
x = acm_data["acc_x"]
y = acm_data["acc_y"]
z = acm_data["acc_z"]
t = acm_data["time"]
for dat, num, axis in zip((x,y,z), range(311, 314), "XYZ"):
plt.subplot(num)
plt.plot(t, dat, ".")
plt.title("%s-axis" %axis)
plt.show()
Which gave me this as the visual output:
Visual output
Which is more readable that color-codes.
Notes:
1) If you want to connect them, remove the "." or change it to "-"
2) This was on Python 3.4
3) If you wanted, you could also add labels on the left and bottom of the graphs.
Related
How to plot this kind of thermal plot in Python? I tried to search for any sample plot like this but didn't find one.
This image I got from the internet. I want to plot something same like this:
FROM
TO
To represent this type of data the canonical solution is, of course, a heat map. Here it is the code to produce both the figures at the top of this post.
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(0, 5, 501)
x = np.linspace(0, 1, 201)[:, None]
T = 50 + (30-6*t)*(4*x*(1-x)) + 4*t
fig, ax = plt.subplots(layout='constrained')
hm = ax.imshow(T, cmap='plasma',
aspect='auto', origin='lower', extent=(0, 5, 0, 1))
fig.colorbar(hm)
def heat_lines(x, t, T, n):
from matplotlib.cm import ScalarMappable
from matplotlib.collections import LineCollection
lx, lt = T.shape
ones = np.ones(lx)
norm = plt.Normalize(np.min(T), np.max(T))
plasma = plt.cm.plasma
fig, ax = plt.subplots(figsize=(1+1.2*n, 9), layout='constrained')
ax.set_xlim((-0.6, n-0.4))
ax.set_ylim((x[0], x[-1]))
ax.set_xticks(range(n))
ax.tick_params(right=False,top=False, bottom=False)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.grid(axis='y')
fig.colorbar(ScalarMappable(cmap=plasma, norm=norm))
dt = round(lt/(n-1))
for pos, ix in enumerate(range(0, len(t)+dt//2, dt)):
points = np.array([ones*pos, x[:,0]]).T.reshape(-1,1,2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, linewidth=72, ec=None,
color=plasma(norm(T[:,ix])))
lc.set_array(T[:,ix])
ax.add_collection(lc)
heat_lines(x, t, T, 6)
I want to generate a figure inside a function, and then be able to add additional stuff to that plot in another function. I would like both figures (the original and the edited one) to be available for future usage. Something like:
import numpy as np
import matplotlib.pyplot as plt
def plot_1():
X, Y = np.meshgrid(np.linspace(0, 10, 100), np.linspace(0, 10, 50))
z = np.random.rand(50, 100)
fig, ax = plt.subplots()
ax.contourf(X, Y, z, cmap="viridis")
return fig
def plot_2(fig):
ax = fig.axes[0]
ax.scatter([2, 5], [1, 4], zorder=2.5, color="r")
return ax
f = plot_1()
f2 = plot_2(f)
However, this changes the original image (which I would like to stay as it originally was) and doesn't return a printable figure in f2. At first I thought the problem was that the scatter plot was not being done but as suggested by Lucas in the comments that was not the actual issue, as it could be solved with a correct value for zorder.
How can I get this right?
If I understand correctly: you want to have two figures, plot the same in both and then only in one make some extra plots.
The way to do this is to create the figures outside the functions and pass the axes to the functions:
import numpy as np
import matplotlib.pyplot as plt
def plot_1(ax):
X, Y = np.meshgrid(np.linspace(0, 10, 100), np.linspace(0, 10, 50))
z = np.random.rand(50, 100)
ax.contourf(X, Y, z, cmap="viridis")
def plot_2(ax):
ax.scatter([2, 5], [1, 4], zorder=2.5, color="r")
fig_1, ax_1 = plt.subplots()
fig_2, ax_2 = plt.subplots()
plot_1(ax_1)
plot_1(ax_2)
plot_2(ax_2)
This will plot the two figures with the contourf but only one with the scatter.
I am scatter ploting data points with a very small marker (see screengrab below). When I use the very small marker ',' the legend is very hard to read (example code taken from here).
(Python 3, Jupyter lab)
How can I increase the size of the marker in the legend. The two versions shown on the above mentioned site do not work:
legend = ax.legend(frameon=True)
for legend_handle in legend.legendHandles:
legend_handle._legmarker.set_markersize(9)
and
ax.legend(markerscale=6)
The two solutions do however work when the marker is set to '.'.
How can I show bigger makers in the legend?
Sample Code from intoli.com:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
You can get 1 pixel sized markers for a plot by setting the markersize to 1 pixel. This would look like
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None")
What the above does is set the marker to a square, set the markersize to the ppi (points per inch) divided by dpi (dots per inch) == dots == pixels, and removes lines and edges.
Then the solution you tried using markerscale in the legend works nicely.
Complete example:
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, marker='s', markersize=72./fig.dpi, mec="None", ls="None",
label=f'Cluster {i + 1}')
ax.legend(markerscale=12)
fig.tight_layout()
plt.show()
According to this discussion, the markersize has no effect when using pixels (,) as marker. How about generating a custom legend instead? For example, by adapting the first example in this tutorial, one can get a pretty decent legend:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
np.random.seed(12)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
for i in range(5):
mean = [np.random.random()*10, np.random.random()*10]
covariance = [ [1 + np.random.random(), np.random.random() - 1], [0, 1 + np.random.random()], ]
covariance[1][0] = covariance[0][1] # must be symmetric
x, y = np.random.multivariate_normal(mean, covariance, 3000).T
plt.plot(x, y, ',', label=f'Cluster {i + 1}')
##generating custom legend
handles, labels = ax.get_legend_handles_labels()
patches = []
for handle, label in zip(handles, labels):
patches.append(mpatches.Patch(color=handle.get_color(), label=label))
legend = ax.legend(handles=patches)
fig.tight_layout()
plt.show()
The output would look like this:
I would like to generate a series of histogram shown below:
The above visualization was done in tensorflow but I'd like to reproduce the same visualization on matplotlib.
EDIT:
Using plt.fill_between suggested by #SpghttCd, I have the following code:
colors=cm.OrRd_r(np.linspace(.2, .6, 10))
plt.figure()
x = np.arange(100)
for i in range(10):
y = np.random.rand(100)
plt.fill_between(x, y + 10-i, 10-i,
facecolor=colors[i]
edgecolor='w')
plt.show()
This works great, but is it possible to use histogram instead of a continuous curve?
EDIT:
joypy based approach, like mentioned in the comment of october:
import pandas as pd
import joypy
import numpy as np
df = pd.DataFrame()
for i in range(0, 400, 20):
df[i] = np.random.normal(i/410*5, size=30)
joypy.joyplot(df, overlap=2, colormap=cm.OrRd_r, linecolor='w', linewidth=.5)
for finer control of colors, you can define a color gradient function which accepts a fractional index and start and stop color tuples:
def color_gradient(x=0.0, start=(0, 0, 0), stop=(1, 1, 1)):
r = np.interp(x, [0, 1], [start[0], stop[0]])
g = np.interp(x, [0, 1], [start[1], stop[1]])
b = np.interp(x, [0, 1], [start[2], stop[2]])
return (r, g, b)
Usage:
joypy.joyplot(df, overlap=2, colormap=lambda x: color_gradient(x, start=(.78, .25, .09), stop=(1.0, .64, .44)), linecolor='w', linewidth=.5)
Examples with different start and stop tuples:
original answer:
You could iterate over your dataarrays you'd like to plot with plt.fill_between, setting colors to some gradient and the line color to white:
creating some sample data:
import numpy as np
t = np.linspace(-1.6, 1.6, 11)
y = np.cos(t)**2
y2 = lambda : y + np.random.random(len(y))/5-.1
plot the series:
import matplotlib.pyplot as plt
import matplotlib.cm as cm
colors = cm.OrRd_r(np.linspace(.2, .6, 10))
plt.figure()
for i in range(10):
plt.fill_between(t+i, y2()+10-i/10, 10-i/10, facecolor = colors[i], edgecolor='w')
If you want it to have more optimized towards your example you should perhaps consider providing some sample data.
EDIT:
As I commented below, I'm not quite sure if I understand what you want - or if you want the best for your task. Therefore here a code which plots besides your approach in your edit two smples of how to present a bunch of histograms in a way that they are better comparable:
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.cm as cm
N = 10
np.random.seed(42)
colors=cm.OrRd_r(np.linspace(.2, .6, N))
fig1 = plt.figure()
x = np.arange(100)
for i in range(10):
y = np.random.rand(100)
plt.fill_between(x, y + 10-i, 10-i,
facecolor=colors[i],
edgecolor='w')
data = np.random.binomial(20, .3, (N, 100))
fig2, axs = plt.subplots(N, figsize=(10, 6))
for i, d in enumerate(data):
axs[i].hist(d, range(20), color=colors[i], label=str(i))
fig2.legend(loc='upper center', ncol=5)
fig3, ax = plt.subplots(figsize=(10, 6))
ax.hist(data.T, range(20), color=colors, label=[str(i) for i in range(N)])
fig3.legend(loc='upper center', ncol=5)
This leads to the following plots:
your plot from your edit:
N histograms in N subplots:
N histograms side by side in one plot:
Here's my python code,
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from matplotlib.pyplot import savefig
a = np.genfromtxt('do_cv.csv', skiprows = 1, delimiter = ',')
for i in xrange(2):
t = a[i+1:(i+1)*60, 2]
z = a[i+1:(i+1)*60, 3]
est_z = a[i+1:(i+1)*60, 6]
figure(i+1)
plt.plot(t, z, 'bo-', t, est_z, 'go-')
plt.xlabel('time')
plt.ylabel('data value')
plt.grid(True)
plt.legend(['sample data', 'estimated sample data'])
plt.savefig('test + str(i).png')
plt.show()
then 2 windows come out, like this,
figure 2 contains plots of figure 1, how to redraw the plot before the second loop begins?
And I only got 1 png file saved in my folder.
How to modify my code and get the result I want? Please give me some suggestions, thanks a lot.
You should write your self a helper function:
def my_plotter(ax, t, z, est_z):
ln1 = ax.plot(t, z, 'bo-', label='sample data')
ln2 = ax.plot(t, est_z, 'go-', label='estimated sample data')
ax.xlabel('time')
ax.ylabel('data value')
ax.grid(True)
ax.legend()
return ln1 + ln2
for i in xrange(2):
# get the data
t = a[i+1:(i+1)*60, 2]
z = a[i+1:(i+1)*60, 3]
est_z = a[i+1:(i+1)*60, 6]
# make the figure
fig, ax = plt.subplots()
# do the plot
my_plotter(ax, t, z, est_Z)
# save
fig.savefig('test_{}.png'.format(i))
Now if you decide you want to put both of these is one figure as sub-plots, all you have to do is:
# make one figure with 2 axes
fig, ax_lst = plt.subplots(1, 2)
for i, ax in zip(xrange(2), ax_lst):
# get the data
t = a[i+1:(i+1)*60, 2]
z = a[i+1:(i+1)*60, 3]
est_z = a[i+1:(i+1)*60, 6]
# do the plot
my_plotter(ax, t, z, est_Z)
# save the figure with both plots
fig.savefig('both.png')
You overwrite your png file every iteration of the loop, that's why you only have one.
plt.savefig('test + str(i).png')
Should be
plt.savefig('test ' + str(i) + '.png')