Two point segment plot in matplotlib - python

How can I plot a two point line segment plot as shown in the following figure
The data is like below
x = [1,2,3,4,5,6]
y = [1.2,1.2,-2.1, -2.1, 4.1, -4.1] #these y values are always in pair such that I need a solid line to connect these equivalent values and then a dotted line between this pair and the next pair.

Does this achieve what you were hoping?
import numpy as np
import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5, 6]
y = [1.2, 1.2, 2.1, 2.1, -4.1, -4.1]
plt.plot(x, y, 'm--')
pair_x_array = np.reshape(x, (-1, 2))
pair_y_array = np.reshape(y, (-1, 2))
for i, pair_x in enumerate(pair_x_array):
pair_y = pair_y_array[i]
plt.plot(pair_x, pair_y, 'm', linewidth=3)
plt.show()

Do you mean something like this?
import pylab
xdata = [0, 1, 2, 3, 4, 5]
ydata = [0, 1, 2, 2, 1, 0]
# Assuming xdata, ydata are of suitable length and type
plots = [pylab.plot(xdata[i:i + 2], ydata[i:i + 2], **whatever_keyword_arguments) for i in xrange(0, len(xdata), 2)]
pylab.show()
Edit after OP edit:
I see what you mean, and it's trivial to add the lines in dashes.
def plot_broken(xseq, yseq, even=True, **plot_kwargs):
size = len(xseq)
assert size == len(yseq)
assert size % 2 == 0
start = 0 if even else 1
return [pylab.plot(xseq[i:i + 2], yseq[i:i + 2], **plot_kwargs)
for i in xrange(start, size, 2)]
plots = plot_broken(xdata, ydata, even=True, color="m",
linestyle="solid")
plots += plot_broken(xdata, ydata, even=False, color="m",
linestyle="dashed")

Related

Plot a triangle contour/surface matplotlib python: tricontourf/meshgrid

Given the following image, where plt.Polygon was used to create the triangle and plt.contourf the contour, i would like to 'cut' off the regions marked in X so that i only get the contour inside the triangle. How would i do this?
I've tried placing NaN values in the mentioned regions but the edges are square and therefore requires a large meshgrid to generate a 'clean' cut between the unwanted and wanted regions. Rather is it more appropiate to use plt.tricontourf? If so how should one approach it?
Thanks #Zephyr for the tricontourf solution, Heres how i solved it without the use of tricontourf and simply a meshgrid
# =============== Define Points =============== #
left_point = np.array([8, 1])
top_point = np.array([10.75, 3.75])
right_point = np.array([13.5, 1])
# =============== Define Left Line Eq: y = mx+c =============== #
left_m = (top_point[-1] - left_point[-1]) / (top_point[0] - left_point[0])
left_c = left_point[-1] - left_m*left_point[0]
# =============== Define Right Line Eq: y = mx+c =============== #
right_m = (right_point[-1] - top_point[-1]) / (right_point[0] - top_point[0])
right_c = right_point[-1] - right_m*right_point[0]
# =============== Generate Spaced Points on Both Lines =============== #
n_points = 100
# x-coordinates on left line
X_left = np.linspace(left_point[0], top_point[0], n_points)
# y-coordinates on left line
Y_left = left_m * X_left + left_c
# x-coordinates on right line
X_right = np.linspace(right_point[0], top_point[0], n_points)
# y-coordinates on right line
Y_right = right_m * X_right + right_c
# Concatenate Left line X and Right line X: [X_left, X_right]
LR_X = np.hstack([X_left[:, None], X_right[:, None]])
# =============== Generate Spaced Points IN BETWEEN points on both lines =============== #
"""
# We can use lists to generate points between each adjacent points on the left/right line
# Then turn them into arrays
# NOTE: Y_left and Y_right are essentially equal so we could just use one of them
# XX = []
# YY = []
# for ii in range(n_points):
# XX.append(np.linspace(LR_X[ii, 0], LR_X[ii, 1], n_points).reshape(1, -1))
# YY.append(Y_left[ii]*np.ones(n_points).reshape(1, -1))
# XX = np.vstack(XX)
# YY = np.vstack(YY)
"""
# Or We could do this (Same thing)
XX = np.meshgrid(np.linspace(LR_X[:, 0], LR_X[:, 1], n_points))[0].reshape(n_points, n_points).T
YY = np.meshgrid([Y_left*np.ones(n_points)]*n_points)[0].reshape(n_points, n_points).T
# Im using a model to predict each point, so i had to flatten it out first
# i.e. XX.shape = (100, 100); YY.shape = (100, 100), WW.shape = (100*100, 2)
WW = np.c_[XX.ravel(), YY.ravel()]
ZZ = model.predict(WW).reshape(XX.shape)
# =============== Contour/Surface Plots =============== #
# Contour plot
fig1 = plt.figure(1, figsize=(8, 6))
ax1 = fig1.add_subplot(111)
levels = np.arange(Y.min(), Y.max())
contour_map = ax1.contourf(XX, YY, ZZ, cmap='viridis')
contour = ax1.contour(XX, YY, ZZ)
cbar = fig1.colorbar(contour_map, )
# Surface Plot
fig2 = plt.figure(2, figsize=(10, 6))
ax2 = fig2.add_subplot(projection='3d')
ax2.plot_surface(XX, YY, ZZ, cmap='viridis')
You could do it with matplotlib.pyplot.tricontourf, but it depends on the way you stored data.
You should have 4 arrays:
x array (1, N) with x coordinates
y array (1, N) with y coordinates
z array (1, N) with z values
triangles list of the index of x and y points which are the vertices of the clipping triangle
x = np.asarray([8, 10, 8, 14, 9, 11, 13, 10, 12, 11])
y = np.asarray([2, 2, 2, 2, 2.56666667, 2.56666667, 2.56666667, 3.13333333, 3.13333333, 3.7])
z = np.cos(2.5*x*x)*np.cos(1.5*y*x)
triangles = [[0, 3, 9]]
Then you can perform triangulation with:
triang = mtri.Triangulation(x, y, triangles)
Complete Code
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
import numpy as np
x = np.asarray([8, 10, 8, 14, 9, 11, 13, 10, 12, 11])
y = np.asarray([2, 2, 2, 2, 2.56666667, 2.56666667, 2.56666667, 3.13333333, 3.13333333, 3.7])
z = np.cos(2.5*x*x)*np.cos(1.5*y*x)
triangles = [[0, 3, 9]]
triang = mtri.Triangulation(x, y, triangles)
fig, ax = plt.subplots()
t = ax.tricontourf(triang, z)
plt.colorbar(t)
plt.show()
If your data are not in this shape, you should re-shaping your array in order to get x, y and z as above described and calculate the triangles indeces list. Please, provide your data if you need help.

How to plot a scatter plot which would also represent the histogram for y value for each value for x

I have a set of X and Y data points (about 20k) that I would like to represent using a scatter plot.
The data set looks something list this
x = [1, 1, 2, 1, 2, 1, 1, 2]
y = [3.1, 3.1, 3.1, 1, 2, 3.1, 1, 2]
(not all values are integers in the data actual data set)
I would like to make a scatter plot with color of where the color would indicate the frequency of a particular value in 'y' for a particular 'x'
For this I tried to calculate the histogram of y for each x value but I always end up with a plot which is wrong. the codes I use are shown below
x = [1, 1, 2, 1, 2, 1, 1, 2]
y = [3.1, 3.1, 3.1, 1, 2, 3.1, 1, 2]
I = []
Y = []
C = []
for i in range (0, len(x)):
if x[i] not in I :
I.append(x[i])
for j in range (0, len(x)):
if x[i] == x[j]:
Y.append(y[j])
u,c = np.unique(Y, return_counts=True)
C.append(c)
Y = []
plt.scatter(x, y, s=70, c=C, cmap='RdYlBu', marker='o', edgecolors='black', linewidth=1, alpha=7)
plt.xlabel('x')
plt.ylabel('y')
plt.colorbar()
the final plot looks like this
final plot
It will be really helpful if someone could tell me where I'm making a mistake or how could I possibly achieve this. I'm very new to python so more explanation is appreciated.
Thank you in advance.
(also will it be possible to make the dot having the same value appear repeatedly with the same color?)
Here is a code that works for you :
import numpy as np
import matplotlib.pyplot as plt
x = np.array([1, 1, 2, 1, 2, 1, 1, 2])
y = np.array([3.1, 3.1, 3.1, 1, 2, 3.1, 1, 2])
X=[]
Y=[]
C=[]
for i in np.unique(x):
new_y = y[np.where(x==i)]
unique,count = np.unique(new_y, return_counts=True)
for j in range(len(unique)):
X.append(i)
Y.append(unique[j])
C.append(count[j])
plt.scatter(X,Y,c=C)
plt.colorbar()
What I do is that for each "unique" value of x I check the values of y using the build in numpy function where. Then my count is not much different from yours.
Here is the result:

plot data in connected horizantla lines on python

I have these array data x and i want to plot horizantal line for each value in it in a specific sub-interval of the range y
please can any one help me ?
x=[2,4,3,5]
;y=np.linspace(0,20,20)
Can be done with a bit of logic and preparing the x and y coordinates-
import matplotlib.pyplot as plt
y = [2, 4, 3, 5]
ys = []
xs = []
step_length = [5, 4, 2, 3]
for i, ycoord in enumerate(y):
ys.extend([ycoord]*(step_length[i]))
xs.extend(list(range(len(xs) - i, len(xs) + step_length[i] - i)))
plt.plot(xs, ys)
plt.show()

Matplotlib pyplot: Is it possible to have exponential axis?

i need "the opposite" of loglog for my pyplot chart.
How can i achive exponential axis?
Data looks like:
x = [1, 2, 4]
y = [1, 2, 4]
z = [2, 2, 2]
quadranten = plt.figure()
s = [20*4**n for n in z]
fig, ax = plt.subplots()
ax.axis([1, 5, 1, 5])
ax.loglog() <-- opposite function?
xstart, xend = ax.get_xlim()
ax.xaxis.set_ticks(np.arange(xstart, xend, 0.712123))
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ystart, yend = ax.get_ylim()
ax.yaxis.set_ticks(np.arange(ystart, yend, 0.712123))
ax.yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
plt.xlabel('x')
plt.ylabel('y')
plt.scatter(x,y,s=s)
plt.show()
The goal is the x axis to have evenly sized steps: 0, 1, 2, 4, 8,...
Same for the y axis with evenly sized steps getting exponentially bigger by a factor (for example two): 0, 1, 2, 4, 8, ...
Is this possible?
Something like this:
loglog takes arguments basex and basey that control the base of the logarithms:
loglog(arange(1,100), arange(1,100)**2, basex=2, basey=2)

Suggestions to plot overlapping lines in matplotlib?

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()

Categories