I am trying to plot hatches over contours lines that
statisfy certian criteria folliwng the example found here. Yet, I got regular contours (the yellow lines) instead of the hatches. Any ideas how to resolve that. Thanks
import matplotlib.pyplot as plt
import numpy as np
# invent some numbers, turning the x and y arrays into simple
# 2d arrays, which make combining them together easier.
x = np.linspace(-3, 5, 150).reshape(1, -1)
y = np.linspace(-3, 5, 120).reshape(-1, 1)
z = np.cos(x) + np.sin(y)
# we no longer need x and y to be 2 dimensional, so flatten them.
x, y = x.flatten(), y.flatten()
fig2, ax2 = plt.subplots()
n_levels = 6
a=ax2.contourf(x, y, z, n_levels)
fig2.colorbar(a)
[m,n]=np.where(z > 0.5)
z1=np.zeros(z.shape)
z1[m,n]=99
cs = ax2.contour(x, y, z1,2,hatches=['','.'])
plt.show()enter code here
Use contourf() with proper parameters to get useful plot with hatching. See important comment within the working code below:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-3, 5, 150).reshape(1, -1)
y = np.linspace(-3, 5, 120).reshape(-1, 1)
z = np.cos(x) + np.sin(y)
x, y = x.flatten(), y.flatten()
fig2, ax2 = plt.subplots()
n_levels = 6
a = ax2.contourf(x, y, z, n_levels)
fig2.colorbar(a)
[m,n] = np.where(z > 0.5)
z1=np.zeros(z.shape)
z1[m, n] = 99
# use contourf() with proper hatch pattern and alpha value
cs = ax2.contourf(x, y, z1 ,3 , hatches=['', '..'], alpha=0.25)
plt.show()
The output plot:
Related
I have 4 arrays x, y, z and T of length n and I want to plot a 3D curve using matplotlib. The (x, y, z) are the points positions and T is the value of each point (which is plotted as color), like the temperature of each point. How can I do it?
Example code:
import numpy as np
from matplotlib import pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
n = 100
cmap = plt.get_cmap("bwr")
theta = np.linspace(-4 * np.pi, 4 * np.pi, n)
z = np.linspace(-2, 2, n)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
T = (2*np.random.rand(n) - 1) # All the values are in [-1, 1]
What I found over the internet:
It's possible to use cmap with scatter like shown in the docs and in this stackoverflow question
ax = plt.gca()
ax.scatter(x, y, z, cmap=cmap, c=T)
The problem is that scatter is a set of points, not a curve.
In this stackoverflow question the solution was divide in n-1 intervals and each interval we use a different color like
t = (T - np.min(T))/(np.max(T)-np.min(T)) # Normalize
for i in range(n-1):
plt.plot(x[i:i+2], y[i:i+2], z[i:i+2], c=cmap(t[i])
The problem is that each segment has only one color, but it should be an gradient. The last value is not even used.
Useful links:
Matplotlib - Colormaps
Matplotlib - Tutorial 3D
This is a case where you probably need to use Line3DCollection. This is the recipe:
create segments from your array of coordinates.
create a Line3DCollection object.
add that collection to the axis.
set the axis limits.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3DCollection
from matplotlib.cm import ScalarMappable
from matplotlib.colors import Normalize
def get_segments(x, y, z):
"""Convert lists of coordinates to a list of segments to be used
with Matplotlib's Line3DCollection.
"""
points = np.ma.array((x, y, z)).T.reshape(-1, 1, 3)
return np.ma.concatenate([points[:-1], points[1:]], axis=1)
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
n = 100
cmap = plt.get_cmap("bwr")
theta = np.linspace(-4 * np.pi, 4 * np.pi, n)
z = np.linspace(-2, 2, n)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
T = np.cos(theta)
segments = get_segments(x, y, z)
c = Line3DCollection(segments, cmap=cmap, array=T)
ax.add_collection(c)
fig.colorbar(c)
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
ax.set_zlim(z.min(), z.max())
plt.show()
Very simple, if I plot x^2+y^2=z it makes this shape on python it will make this shape:
When I would like to plot it this way:
Below is my code, I am new so I copied it from the internet and have changed the line with the function to plot.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-4*np.pi,4*np.pi,50)
y = np.linspace(-4*np.pi,4*np.pi,50)
z = x**2+y**2
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(x,y,z)
plt.show()
Also, how do I make it more high definition and smooth, this is a graph of z=sin(x)
You need to define a 2D mathematical domain with numpy.meshgrid, then you can compute the surface on that domain:
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2
In order to increase the smoothness of the surface, you have in increase the number of point N you use to compute x and y arrays:
Complete code
import matplotlib.pyplot as plt
import numpy as np
N = 50
x = np.linspace(-4*np.pi, 4*np.pi, N)
y = np.linspace(-4*np.pi, 4*np.pi, N)
X, Y = np.meshgrid(x, y)
Z = X**2 + Y**2
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z)
plt.show()
With matplotlib I am trying to plot 3D data as a 2D colormap. Each point has a x and a y coordinate, and a 'height' z. This height should determine the color a certain x/y region is colored in.
Here is the code I have been trying:
import random
import numpy as np
import matplotlib.pyplot as plt
x = []
y = []
z = []
for index in range(100):
a = random.random()
b = random.random()
c = np.exp(-a*a - b*b)
x.append(a)
y.append(b)
z.append(c)
cmap = plt.get_cmap('PiYG')
fig, ax = plt.subplots()
ax.pcolormesh(x, y, z, cmap=cmap)
But it gives an error
ValueError: not enough values to unpack (expected 2, got 1)
Maybe I am trying the wrong thing?
Remark: The three lists x,y,z and calculated for the example above, but in reality I have just three lists with "random" numbers in it I want to vizualize. I cannot calculate z given x and y.
I could also use imshow to create the plot I want, but I have to convert my original data into a matrix first. Maybe there is a function I can use?
pcolormesh might not be the choice for this kind of problem. pcolormesh expects ordered cell edges as data rather than random data points. You could do this if you know your grid before hand e.g.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 1, 51)
# meshgrid makes a 2D grid of points
xx, yy = np.meshgrid(x, x)
z = np.exp(-xx**2 - yy*2)
fig, ax = plt.subplots()
ax.pcolormesh(xx, yy, z, cmap="PiYG")
which will give you
Alternatively, you could use one of the tri functions such as tripcolor with your existing setup
import random
import numpy as np
import matplotlib.pyplot as plt
x = []
y = []
z = []
for index in range(100):
a = random.random()
b = random.random()
c = np.exp(-a*a - b*b)
x.append(a)
y.append(b)
z.append(c)
fig, ax = plt.subplots()
ax.tripcolor(x, y, z, cmap="PiYG")
which will give
Note it would be simpler to use np.random to generate your data
x, y = np.random.random(size=(2, 100))
z = np.exp(-x**2 - y**2)
fig, ax = plt.subplots()
ax.tripcolor(x, y, z, cmap="PiYG")
There is an issue with x, y and z shapes: they have to be 2D arrays (matrices) but they are 1-dimensional.
In order to generate x and y axis, you could use:
x = []
y = []
for index in range(100):
x.append(random.random())
y.append(random.random())
Then you have to create a meshgrid:
X, Y = np.meshgrid(x, y)
Finally you can compute Z over the meshgrid:
Z = np.exp(-X**2 - Y**2)
In this way, your code:
cmap = plt.get_cmap('PiYG')
fig, ax = plt.subplots()
ax.pcolormesh(X, Y, Z, cmap=cmap)
gives:
If you you cannot compute Z on the meshgrid, then you should not use pcolormesh.
Some alternative could be:
3D scatterplot:
import random
import numpy as np
import matplotlib.pyplot as plt
x = []
y = []
z = []
for index in range(100):
a = random.random()
b = random.random()
c = np.exp(-a*a - b*b)
x.append(a)
y.append(b)
z.append(c)
cmap = plt.get_cmap('PiYG')
fig = plt.figure()
ax = fig.add_subplot(projection = '3d')
ax.scatter(x, y, z, cmap=cmap)
plt.show()
2D colored scatterplot:
import random
import numpy as np
import matplotlib.pyplot as plt
x = []
y = []
z = []
for index in range(100):
a = random.random()
b = random.random()
c = np.exp(-a*a - b*b)
x.append(a)
y.append(b)
z.append(c)
cmap = plt.get_cmap('PiYG')
plt.style.use('seaborn-darkgrid')
fig, ax = plt.subplots()
ax.scatter(x, y, c = z, cmap=cmap)
plt.show()
With the following code I have obtained the following contour map:
fig, ax = plt.subplots()
x = np.arange(431)
y = np.arange(225)
Y, X = np.meshgrid(y, x)
values = df["Appearance_percentage"].values
values2d = np.reshape(values, (431, 225))
ax.set_ylim(225, 0)
plt.style.use('seaborn-white')
ax.set_title('Mapa contour de probabilitat de trobar núvols')
plt.contour(X, Y, values2d, 30, cmap='RdGy')
plt.colorbar()
plt.savefig("contourmap.png")
I would like to know if I could fill the areas between the lines so that there are no white spaces in the color bar and the map is more attractive.
I tried doing df["Appearance_percentage_contourmap"] = round(df["Appearance_percentage"]) and then values = df["Appearance_percentage_contourmap"].values and I'm still obtaining the same map with lots of white areas.
Just replace plt.contour with plt.contourf, where the "f" at the end means "fill".
Here is an example:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(100)
y = np.arange(100)
Y, X = np.meshgrid(y, x)
values = np.outer(x, y)
fig, ax = plt.subplots()
plt.contourf(X, Y, values, 30, cmap='RdGy')
plt.colorbar()
You may also want to emphasis the contour lines with:
x = np.arange(100)
y = np.arange(100)
Y, X = np.meshgrid(y, x)
values = np.outer(x, y)
fig, ax = plt.subplots()
c1 = plt.contourf(X, Y, values, 30, cmap='RdGy')
c2 = plt.contour(X, Y, values, 30, cmap='Greys')
plt.colorbar(c1)
plt.contour() creates an isoline connecting all the places with an equal interpolated value. So, it searches places where the appearance is e.g. 6% and connects these with a line. If you set levels=30 there will be 30 such lines drawn. plt.contour() does a lot of effort to create a colorbar that shows the value for each line. If you don't want such a colorbar, you can create a custom colorbar using the same values.
You can create a custom colorbar as follows:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots()
x = np.arange(431)
y = np.arange(225)
Y, X = np.meshgrid(y, x)
values = np.random.randn(431, 225).cumsum(axis=0).cumsum(axis=1).ravel()
values -= values.min()
values2d = np.reshape(values, (431, 225))
ax.set_ylim(225, 0)
plt.style.use('seaborn-white')
ax.set_title('Mapa contour de probabilitat de trobar núvols')
plt.contour(X, Y, values2d, 30, cmap='RdGy')
sm = ScalarMappable(norm=plt.Normalize(values.min(), values.max()), cmap='RdGy')
plt.colorbar(sm)
plt.show()
PS:Please don't round the values (as in df["Appearance_percentage_contourmap"] = round(df["Appearance_percentage"])), because that introduces artificial inaccuracies.
When drawing a contour plot with Python/Matplotlib, the default behaviour (for 1 color) is that negative values are dashed. This is a desired feature for me. However, if I set the color of the lines, all of them are drawn solid. I would like to combine the dashed negatives and custom colors.
How can I plot colored lines, and keep the negative-dahsed style?
Below, I copy (modifying a bit), an example from this tutorial: https://www.oreilly.com/library/view/python-data-science/9781491912126/ch04.html
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
# Default: 1 color, negatives are dashed
plt.contour(X, Y, Z, colors='black')
plt.show()
# Set colormap: all lines are solid
plt.contour(X, Y, Z, cmap='RdBu')
plt.show()
# Set individual colors: all solid lines
plt.contour(X, Y, Z, colors=['b','b','b','r','r','r','r','r'])
plt.show()
Defalut: negatives are dashed.
Set colors via colormap: all have become solid.
Set individual colors: all solid again. I would like the blue lines here to be dashed, automatically, since they are negative values.
Unfortunately, the feature of different linestyles for negative values is not exposed to the user. It is bound to whether or not a single color is used for the lines. This toggles a property monochrome, which in turn decides whether or not to change the linestyle.
A quick hack is hence to set the monochrome attribute to True and reset the linesstyles.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
cntr = plt.contour(X, Y, Z, cmap='RdBu')
cntr.monochrome = True
for col, ls in zip(cntr.collections, cntr._process_linestyles()):
col.set_linestyle(ls)
plt.show()
Since this uses a private ._process_linestyles() attribute, it would not be recommended to use it in production code; but rather use #WarrenWeckesser's answer or the option below.
Here I would like to point to the option to set the linestyles a priori, depending on the levels:
import matplotlib.pyplot as plt
import matplotlib.ticker
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
loc = matplotlib.ticker.MaxNLocator(7)
lvls = loc.tick_values(Z.min(), Z.max())
cntr = plt.contour(X, Y, Z, levels=lvls, cmap='RdBu',
linestyles=np.where(lvls >= 0, "-", "--"))
plt.show()
Here's one way you can do it. (A matplotlib guru might be able to recommend an easier way.)
This example uses a colormap to set the colors, and then changes the linestyles after the contour plot is created. A dashed line is used for negative contours, a dotted line for 0, and a solid line for positive.
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
def f(x, y):
return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
Z = f(X, Y)
# Contour with a specified colormap.
result = plt.contour(X, Y, Z, cmap='RdBu')
# Override the linestyles based on the levels.
for line, lvl in zip(result.collections, result.levels):
if lvl < 0:
line.set_linestyle('--')
elif lvl == 0:
line.set_linestyle(':')
else:
# Optional; this is the default.
line.set_linestyle('-')
# Optional--this makes the 0 contour more visible with the
# chosen colormap.
ax = plt.gca()
ax.set_facecolor('#d0d0d0')
plt.show()
Here's the result: