Related
I'm doing a small project which requires to resolve a bug in matplotlib in order to fix zorders of some ax.patches and ax.collections. More exactly, ax.patches are symbols rotatable in space and ax.collections are sides of ax.voxels (so text must be placed on them). I know so far, that a bug is hidden in draw method of mpl_toolkits.mplot3d.Axes3D: zorder are recalculated each time I move my diagram in an undesired way. So I decided to change definition of draw method in these lines:
for i, col in enumerate(
sorted(self.collections,
key=lambda col: col.do_3d_projection(renderer),
reverse=True)):
#col.zorder = zorder_offset + i #comment this line
col.zorder = col.stable_zorder + i #add this extra line
for i, patch in enumerate(
sorted(self.patches,
key=lambda patch: patch.do_3d_projection(renderer),
reverse=True)):
#patch.zorder = zorder_offset + i #comment this line
patch.zorder = patch.stable_zorder + i #add this extra line
It's assumed that every object of ax.collection and ax.patch has a stable_attribute which is assigned manually in my project. So every time I run my project, I must be sure that mpl_toolkits.mplot3d.Axes3D.draw method is changed manually (outside my project). How to avoid this change and override this method in any way inside my project?
This is MWE of my project:
import matplotlib.pyplot as plt
import numpy as np
#from mpl_toolkits.mplot3d import Axes3D
import mpl_toolkits.mplot3d.art3d as art3d
from matplotlib.text import TextPath
from matplotlib.transforms import Affine2D
from matplotlib.patches import PathPatch
class VisualArray:
def __init__(self, arr, fig=None, ax=None):
if len(arr.shape) == 1:
arr = arr[None,None,:]
elif len(arr.shape) == 2:
arr = arr[None,:,:]
elif len(arr.shape) > 3:
raise NotImplementedError('More than 3 dimensions is not supported')
self.arr = arr
if fig is None:
self.fig = plt.figure()
else:
self.fig = fig
if ax is None:
self.ax = self.fig.gca(projection='3d')
else:
self.ax = ax
self.ax.azim, self.ax.elev = -120, 30
self.colors = None
def text3d(self, xyz, s, zdir="z", zorder=1, size=None, angle=0, usetex=False, **kwargs):
d = {'-x': np.array([[-1.0, 0.0, 0], [0.0, 1.0, 0.0], [0, 0.0, -1]]),
'-y': np.array([[0.0, 1.0, 0], [-1.0, 0.0, 0.0], [0, 0.0, 1]]),
'-z': np.array([[1.0, 0.0, 0], [0.0, -1.0, 0.0], [0, 0.0, -1]])}
x, y, z = xyz
if "y" in zdir:
x, y, z = x, z, y
elif "x" in zdir:
x, y, z = y, z, x
elif "z" in zdir:
x, y, z = x, y, z
text_path = TextPath((-0.5, -0.5), s, size=size, usetex=usetex)
aff = Affine2D()
trans = aff.rotate(angle)
# apply additional rotation of text_paths if side is dark
if '-' in zdir:
trans._mtx = np.dot(d[zdir], trans._mtx)
trans = trans.translate(x, y)
p = PathPatch(trans.transform_path(text_path), **kwargs)
self.ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, z=z, zdir=zdir)
p.stable_zorder = zorder
return p
def on_rotation(self, event):
vrot_idx = [self.ax.elev > 0, True].index(True)
v_zorders = 10000 * np.array([(1, -1), (-1, 1)])[vrot_idx]
for side, zorder in zip((self.side1, self.side4), v_zorders):
for patch in side:
patch.stable_zorder = zorder
hrot_idx = [self.ax.azim < -90, self.ax.azim < 0, self.ax.azim < 90, True].index(True)
h_zorders = 10000 * np.array([(1, 1, -1, -1), (-1, 1, 1, -1),
(-1, -1, 1, 1), (1, -1, -1, 1)])[hrot_idx]
sides = (self.side3, self.side2, self.side6, self.side5)
for side, zorder in zip(sides, h_zorders):
for patch in side:
patch.stable_zorder = zorder
def voxelize(self):
shape = self.arr.shape[::-1]
x, y, z = np.indices(shape)
arr = (x < shape[0]) & (y < shape[1]) & (z < shape[2])
self.ax.voxels(arr, facecolors=self.colors, edgecolor='k')
for col in self.ax.collections:
col.stable_zorder = col.zorder
def labelize(self):
self.fig.canvas.mpl_connect('motion_notify_event', self.on_rotation)
s = self.arr.shape
self.side1, self.side2, self.side3, self.side4, self.side5, self.side6 = [], [], [], [], [], []
# labelling surfaces of side1 and side4
surf = np.indices((s[2], s[1])).T[::-1].reshape(-1, 2) + 0.5
surf_pos1 = np.insert(surf, 2, self.arr.shape[0], axis=1)
surf_pos2 = np.insert(surf, 2, 0, axis=1)
labels1 = (self.arr[0]).flatten()
labels2 = (self.arr[-1]).flatten()
for xyz, label in zip(surf_pos1, [f'${n}$' for n in labels1]):
t = self.text3d(xyz, label, zdir="z", zorder=10000, size=1, usetex=True, ec="none", fc="k")
self.side1.append(t)
for xyz, label in zip(surf_pos2, [f'${n}$' for n in labels2]):
t = self.text3d(xyz, label, zdir="-z", zorder=-10000, size=1, usetex=True, ec="none", fc="k")
self.side4.append(t)
# labelling surfaces of side2 and side5
surf = np.indices((s[2], s[0])).T[::-1].reshape(-1, 2) + 0.5
surf_pos1 = np.insert(surf, 1, 0, axis=1)
surf = np.indices((s[0], s[2])).T[::-1].reshape(-1, 2) + 0.5
surf_pos2 = np.insert(surf, 1, self.arr.shape[1], axis=1)
labels1 = (self.arr[:, -1]).flatten()
labels2 = (self.arr[::-1, 0].T[::-1]).flatten()
for xyz, label in zip(surf_pos1, [f'${n}$' for n in labels1]):
t = self.text3d(xyz, label, zdir="y", zorder=10000, size=1, usetex=True, ec="none", fc="k")
self.side2.append(t)
for xyz, label in zip(surf_pos2, [f'${n}$' for n in labels2]):
t = self.text3d(xyz, label, zdir="-y", zorder=-10000, size=1, usetex=True, ec="none", fc="k")
self.side5.append(t)
# labelling surfaces of side3 and side6
surf = np.indices((s[1], s[0])).T[::-1].reshape(-1, 2) + 0.5
surf_pos1 = np.insert(surf, 0, self.arr.shape[2], axis=1)
surf_pos2 = np.insert(surf, 0, 0, axis=1)
labels1 = (self.arr[:, ::-1, -1]).flatten()
labels2 = (self.arr[:, ::-1, 0]).flatten()
for xyz, label in zip(surf_pos1, [f'${n}$' for n in labels1]):
t = self.text3d(xyz, label, zdir="x", zorder=-10000, size=1, usetex=True, ec="none", fc="k")
self.side6.append(t)
for xyz, label in zip(surf_pos2, [f'${n}$' for n in labels2]):
t = self.text3d(xyz, label, zdir="-x", zorder=10000, size=1, usetex=True, ec="none", fc="k")
self.side3.append(t)
def vizualize(self):
self.voxelize()
self.labelize()
plt.axis('off')
arr = np.arange(60).reshape((2,6,5))
va = VisualArray(arr)
va.vizualize()
plt.show()
This is an output I get after external change of ...\mpl_toolkits\mplot3d\axes3d.py file:
This is an output (an unwanted one) I get if no change is done:
What you want to achieve is called Monkey Patching.
It has its downsides and has to be used with some care (there is plenty of information available under this keyword). But one option could look something like this:
from matplotlib import artist
from mpl_toolkits.mplot3d import Axes3D
# Create a new draw function
#artist.allow_rasterization
def draw(self, renderer):
# Your version
# ...
# Add Axes3D explicitly to super() calls
super(Axes3D, self).draw(renderer)
# Overwrite the old draw function
Axes3D.draw = draw
# The rest of your code
# ...
Caveats here are to import artist for the decorator and the explicit call super(Axes3D, self).method() instead of just using super().method().
Depending on your use case and to stay compatible with the rest of your code you could also save the original draw function and use the custom only temporarily:
def draw_custom():
...
draw_org = Axes3D.draw
Axes3D.draw = draw_custom
# Do custom stuff
Axes3D.draw = draw_org
# Do normal stuff
I am very confused about how to add color bar to my 3D/2D vector fields.
My source code is below:
import copy
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import fieldmap_utils
data = {}
dot = {
"ampX":0,
"ampY":0,
"ampZ":0,
"phaseX":0,
"phaseY":0,
"phaseZ":0,
}
location = {
"X":[],
"Y":[],
"Z":[]
}
def buildFrame(origin, orientation, length):
body = np.dot(orientation, length).tolist()
temp = origin.copy()
for x in body:
temp.append(x)
return temp
dic = fieldmap_utils.load_single_ampphase("EM averaged.csv")
lst = list(dic.index.get_level_values('freq (hz)').unique())
lst.sort()
print("List of available frequencies: ")
for each_freq in lst:
print(each_freq,end = " ")
print()
freq = input("Enter selected frequency: ")
dic = dic[dic.index.get_level_values('freq (hz)') == int(freq)]
for index, row in dic.iterrows():
# Looks like this:
# data[(x,y,z)] = {"ampX":0,,"ampY":0,"ampZ":0,"phaseX":0,"phaseY":0,"phaseZ":0}
data[(index[4][0], index[4][1], index[4][2])] = copy.deepcopy(dot)
for index, row in dic.iterrows():
if (index[7] == "TARGET-X"):
data[(index[4][0], index[4][1], index[4][2])]["ampX"] = row['ampl']
data[(index[4][0], index[4][1], index[4][2])]["phaseX"] = row['phase_deg']
if (index[7] == "TARGET-Y"):
data[(index[4][0], index[4][1], index[4][2])]["ampY"] = row['ampl']
data[(index[4][0], index[4][1], index[4][2])]["phaseY"] = row['phase_deg']
if (index[7] == "TARGET-Z"):
data[(index[4][0], index[4][1], index[4][2])]["ampZ"] = row['ampl']
data[(index[4][0], index[4][1], index[4][2])]["phaseZ"] = row['phase_deg']
colorMap = cm.get_cmap('Greys')
fig = plt.figure(figsize=(12,12))
ax3D = fig.add_subplot(2, 2, 1, projection = '3d')
axX = fig.add_subplot(2, 2, 2)
axY = fig.add_subplot(2, 2, 3)
axZ = fig.add_subplot(2, 2, 4)
positions = list(data.keys())
xloc = set()
yloc = set()
zloc = set()
for each_position in positions:
xloc.add(each_position[0])
yloc.add(each_position[1])
zloc.add(each_position[2])
xlst = list(xloc)
ylst = list(yloc)
zlst = list(zloc)
xlst.sort()
ylst.sort()
zlst.sort()
print("Unique X coordinates:")
for each_x in xlst:
print(each_x,end = " ")
print()
sliceX = int(input("Enter X slice position: "))
print("Unique Y coordinates:")
for each_y in xlst:
print(each_y,end = " ")
print()
sliceY = int(input("Enter Y slice position: "))
print("Unique Z coordinates:")
for each_z in zlst:
print(each_z,end = " ")
print()
sliceZ = int(input("Enter Z slice position: "))
scale = 500
for position in positions:
x, y, z = position
u, v, w = data[position]["ampX"]*scale, data[position]["ampY"]*scale, data[position]["ampZ"]*scale
# orientation = [[data[position]["ampX"], 0, 0],
# [0, data[position]["ampY"], 0],
# [0, 0, data[position]["ampZ"]]]
# x, y, z, u, v, w = buildFrame([position[0], position[1], position[2]], orientation, 5000)
ax3D.quiver(x, y, z, u, v, w)
if x == sliceX:
axX.quiver(y, z, v, w)
if y == sliceY:
axY.quiver(x, z, u, w)
if z == sliceZ:
axZ.quiver(x, y, u, v)
#, cmap = colorMap
ax3D.view_init(azim=50, elev=25)
ax3D.set_xlabel('X')
ax3D.set_ylabel('Y')
ax3D.set_zlabel('Z')
ax3D.set_xlim([-275, 300])
ax3D.set_ylim([-275, 450])
ax3D.set_zlim([0, 500])
axX.set_title('Looking from X-axis')
axX.set_xlabel('Y')
axX.set_ylabel('Z')
axX.set_xlim([-275, 425])
axX.set_ylim([0, 500])
axY.set_title('Looking from Y-axis')
axY.set_xlabel('X')
axY.set_ylabel('Z')
axY.set_xlim([-275, 300])
axY.set_ylim([0, 500])
axZ.set_title('Looking from Z-axis')
axZ.set_xlabel('X')
axZ.set_ylabel('Y')
axZ.set_xlim([-275, 300])
axZ.set_ylim([-275, 450])
# plt.savefig('demo', dpi = 1200)
plt.show()
The code isn't optimized or perfect, and it is only used as a demo.
However, I am really confused about how should I add 1 color bar to the 3 2D quiver plots.
I have read through some documentations of matlibplot, but I still don't have a decent idea of how to add color bar. I tried to use the same method that I did in scatter plot, but it didn't work out either.
Thank you guys for helping!
I have some data (A,B) and have used seaborn to make a contour plot of it.
import pandas as pd
import seaborn as sns
# Dataframe 1
df_1 = pd.DataFrame({'A':[1,2,1,2,3,4,2,1,4], 'B': [2,1,2,1,2,3,4,2,1]})
# Plot A v B
ax = sns.kdeplot(df_1["A"], df_1["B"])
I would like to get the cumulative count please (C). I’d like to make a new plot with C on the Y axis, A on the X axis and contours of B. I think that if I could start off by making a new dataframe of A,B,H where H was the count (the height of the volcano) then that might be a start. The resulting plot might look a bit like this:
I think I've worked it out but this solution is messy:
import pandas as pd
import numpy as np
from scipy import stats
from itertools import chain
Fruit = 9 # How many were there?
# Dataframe 1
df_1 = pd.DataFrame({'A':[1,2,1,2,3,4,2,1,4], 'B': [2,1,2,1,2,3,4,2,1]})
m1 = df_1["A"]
m2 = df_1["B"]
xmin = 0
xmax = 5
ymin = 0
ymax = 5
# Kernel density estimate:
X, Y = np.mgrid[xmin:xmax:5j, ymin:ymax:5j]
positions = np.vstack([X.ravel(), Y.ravel()])
values = np.vstack([m1, m2])
kernel = stats.gaussian_kde(values)
H = np.reshape(kernel(positions).T, X.shape)
# Re-jig it
X = X.reshape((25, 1))
Y = Y.reshape((25, 1))
H = H.reshape((25, 1))
X_L = list(chain.from_iterable(X))
Y_L = list(chain.from_iterable(Y))
H_L = list(chain.from_iterable(H))
df_2 = pd.DataFrame({'A': X_L, 'B': Y_L, 'H': H_L})
# Find the cumulative count C
df_2 = df_2.sort_values('B')
C = np.cumsum(H)
C = C.reshape((25, 1))
C_L = list(chain.from_iterable(C))
df_2['C'] = pd.DataFrame(C_L, index=df_2.index)
# Scale C
Max_C = np.amax(C)
df_2.loc[:,'C'] *= Fruit / Max_C
# Break it down to constant B
df_2_B_0 = df_2[df_2['B'] == 0]
df_2_B_1 = df_2[df_2['B'] == 1]
df_2_B_2 = df_2[df_2['B'] == 2]
df_2_B_3 = df_2[df_2['B'] == 3]
df_2_B_4 = df_2[df_2['B'] == 4]
# Plot A v C
ax = df_2_B_0.plot('A','C', label='0')
df_2_B_1.plot('A','C',ax=ax, label='1')
df_2_B_2.plot('A','C',ax=ax, label='2')
df_2_B_3.plot('A','C',ax=ax, label='3')
df_2_B_4.plot('A','C',ax=ax, label='4')
plt.ylabel('C')
plt.legend(title='B')
I have data sets columns of features x1 and x2 and class y which has value either 0 or 1. I want to plot x1 and x2 in scatter plot such that values y == 1 will appear as "+" and values y == 0 will appear as "o".
x1 = np.array(100)
x2 = np.array(100)
#y = array of length 100 either with value 1 or 0
plt.scatter(x1, x2, y=1, marker='+')
plt.scatter(x1, x2, y=0, marker='o')
plt.show()
Any suggestions?
You can just index your x1 and x2 arrays using the condition of y==0 or y==1:
plt.scatter(x1[y==1], x2[y==1], marker='+')
plt.scatter(x1[y==0], x2[y==0], marker='o')
Use np.where to get the indices of where the y-array is 0 or 1, and then plot them accordingly. Below is an example
import matplotlib.pyplot as plt
import numpy as np
plt.close('all')
x = np.arange(100)
y = np.random.randint(0, 2, 100)
arg_0 = np.where(y == 0)
arg_1 = np.where(y == 1)
fig, ax = plt.subplots()
ax.scatter(x[arg_0], y[arg_0], marker='o')
ax.scatter(x[arg_1], y[arg_1], marker='+')
ax.set_ylim(-0.1, 1.1)
fig.show()
How can I build a graph using this function? Thanks! :) I tried, but I am getting confused how should I use: https://stackoverflow.com/questions/30553585/graphing-a-parabola-using-matplotlib-in-python#=
def quadratic (a, b, c):
try:
d = b**2-4*a*c
convex_point = -b/(2*a)
if(d == 0):
convex_point = -b/(2*a) #it is the x-interceptor when det is 0
print('the convex point is at',convex_point, 'on the x-axis | the parabola intersect the y-axis at',c, '| the determinant is 0');
elif(d < 0):
print('Determinant is', d,'and if determinant is smaller than zero, there is no real solution');
else:
x_positive = (-b+ math.sqrt(d))/(2*a); # y = 0
x_negative = (-b- math.sqrt(d))/(2*a); # y = 0
print('the convex points is at',convex_point, 'on the x-axis |x_positive',x_positive,' |x_negative',x_negative,'| the parabola intersect the y-axis at',c)
except:
print('try: import math');
def quadratic (a, b, c):
try:
import matplotlib.pyplot as plt
import math
import numpy as np
d = b**2-4*a*c
convex_point = -b/(2*a)
x = np.linspace(-50, 50, 1000);
y = a**2 + b*x + c
fig, ax = plt.subplots();
ax.plot(x, y)
if(d == 0):
convex_point = -b/(2*a) #it is the x-interceptor when det is 0
print('the convex point is at',convex_point, 'on the x-axis | the parabola intersect the y-axis at',c, '| the determinant is 0');
elif(d < 0):
print('Determinant is', d,'and if determinant is smaller than zero, there is no real solution');
else:
x_positive = (-b+ math.sqrt(d))/(2*a); # y = 0
x_negative = (-b- math.sqrt(d))/(2*a); # y = 0
print('the convex points is at',convex_point, 'on the x-axis |x_positive',x_positive,' |x_negative',x_negative,'| the parabola intersect the y-axis at',c);
except:
print('try: import math')
except it does not show the graph :S
def quadratic (a, b, c):
try:
import matplotlib.pyplot as plt
import math
import numpy as np
d = b**2-4*a*c
convex_point = -b/(2*a)
x = np.linspace(-10, 10, 1000);
y = a**2 + b*x + c
fig, ax = plt.subplots();
ax.plot(x, y);
plt.show()
if(d == 0):
convex_point = -b/(2*a) #it is the x-interceptor when det is 0
print('the convex point is at',convex_point, 'on the x-axis | the parabola intersect the y-axis at',c, '| the determinant is 0');
elif(d < 0):
print('Determinant is', d,'and if determinant is smaller than zero, there is no real solution');
else:
x_positive = (-b+ math.sqrt(d))/(2*a); # y = 0
x_negative = (-b- math.sqrt(d))/(2*a); # y = 0
print('the convex points is at',convex_point, 'on the x-axis |x_positive',x_positive,' |x_negative',x_negative,'| the parabola intersect the y-axis at',c);
except:
print('try: import math')
This code work :)