Related
I cannot make it clear for me, how pyplot trisurf works. All the examples I have seen on the Internet use numpy, pandas and other stuff impeding understanding this tool
Pyplot docs say it requires X, Y and Z as 1D arrays. But if I try to provide them, it issues a RuntimeError: Error in qhull Delaunay triangulation calculation: singular input data (exitcode=2); use python verbose option (-v) to see original qhull error. I tried using python list and numpy arange
What are exactly those 1D arrays the tool wants me to provide?
plot_trisurf, when no explicit triangles are given, connects nearby 3D points with triangles to form some kind of surface. X is a 1D array (or a list) of the x-coordinates of these points (similar for Y and Z).
It doesn't work too well when all points lie on the same 3D line. For example, setting all X, Y and Z to [1, 2, 3] will result in a line, not a triangle. P1=(1,1,1), P2=(2,2,2), P3=(3,3,3). The n'th point will use the n'th x, the n'th y and the n'th z. A simple example would be ´ax.plot_trisurf([0, 1, 1], [0, 0, 1], [1, 2, 3])`.
Here is an example:
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
from math import sin, cos, pi
fig = plt.figure(figsize=(14, 9))
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
ax1.plot_trisurf([0, 1, 1], [0, 0, 1], [1, 2, 3],
facecolor='cornflowerblue', edgecolor='crimson', alpha=0.4, linewidth=4, antialiased=True)
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
N = 12
X = [0] + [sin(a * 2 * pi / N) for a in range(N)]
Y = [0] + [cos(a * 2 * pi / N) for a in range(N)]
Z = [1] + [0 for a in range(N)]
ax2.plot_trisurf(X, Y, Z,
facecolor='cornflowerblue', edgecolor='crimson', alpha=0.4, linewidth=4, antialiased=True)
plt.show()
I have two matrix, x and y. x has size of 10 rows and 50 columns, and so is y.
My data is row-to-row paired. It means that the
x[0][:] <-> y[0][:]
x[1][:] <-> y[1][:]
x[2][:] <-> y[2][:]
......
x[49][:] <-> y[0][:]
When I use following command to do the plot, the
plot(x[:][:],y[:][:],'b-o')
or
plot(x,y,'b-o')
to do the plot, the '-' connects the dots in horizontal direction like following:
However, when I do only plot one row of signal:
plot(x[0][:],y[0][:],'b-o')
it looks correct:
I would like for the '-' to connect the dots in a horizontal fashion. Something like this:
in stead of doing a for loop, how do I do it in matrix format? Thanks.
Make some data to demonstrate.
import numpy as np
from matplotlib import pyplot as plt
x = np.matrix(
[
[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4]
]
)
y = x.transpose()
# Vertical Lines of grid:
plt.plot(x, y, 'b-o')
plt.show()
# Horizontal Lines
plt.plot(x, y, 'b-o')
plt.show()
# Together (this is what I think you want)
plt.plot(y, x, 'b-o')
plt.plot(x, y, 'b-o')
plt.show()
If you try to concatenate them to do it in one large matrix it does some seemingly silly things by connecting a couple of points that we really do not want connected.
# sillyness
x1 = np.concatenate((x, y), axis=0)
y1 = np.concatenate((y, x), axis=0)
plt.plot(x1, y1, 'b-o')
plt.show()
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()
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:
I'm trying to start 2D contour plot for a flow net and I'm having trouble getting the initial grid to show up properly.
Given the number of columns and the number of rows, how can I write a function that will plot a grid so that all points in the given range appear?
I tried plotting for 4 columns and 3 rows of points by doing this:
r = 3
c = 4
x = [i for i in range(c)]
y = [i for i in range(r)]
plot(x,y,'ro')
grid()
show()
and get this error:
'ValueError: x and y must have same first dimension'
So I tried testing it on a 4x4 grid and got this and I get close to what I want, however it only plots points (0,0), (1,1), (2,2), and (3,3)
However, I also want the points (0,0), (1,0), (2,0), (3,0), (1,0), (1,1)...(3,2), (3,3) to appear, as I will later need to plot vectors from this point indicating the direction of flow for my flow net.
Sorry, I know my terminology isn't that great. Does anyone know how to do this and how to make it work for grids that aren't square?
You could use itertools.product to generate the desired points.
Use plt.scatter to plot the points
Use plt.quiver to plot the vector field. (Relevant code taken from these SO answers)
import numpy as np
import matplotlib.pyplot as plt
import itertools
r = 3
c = 4
x = np.linspace(0, c, c+1)
y = np.linspace(0, r, r+1)
pts = itertools.product(x, y)
plt.scatter(*zip(*pts), marker='o', s=30, color='red')
X, Y = np.meshgrid(x, y)
deg = np.arctan(Y**3 - 3*Y-X)
QP = plt.quiver(X, Y, np.cos(deg), np.sin(deg))
plt.grid()
plt.show()
r = 3
c = 4
x = [i % c for i in range(r*c)]
y = [i / c for i in range(r*c)]
print x
print y
Gives:
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]
[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2]
When used to draw graph as you did it produces desired result.
The first two arguments specify your x and y components. So the number of points must match. I think what you want is something like:
from itertools import product
import matplotlib.pyplot as plt
points = np.array(list(product(range(3),range(4))))
plt.plot(points[:,0],points[:,1],'ro')
plt.show()