Cannot save animation in python - python-3.x

import numpy as np
import pandas as pd
import matplotlib
# matplotlib.use("Agg")
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.colors import cnames
from matplotlib import animation
t_start = 2 #start frame
t_end = 1711 #end frame
data = pd.read_csv('Sub6_cylinder0009_index_flex_processed.csv')
df = data.loc[t_start:t_end,'FT1X':'CMC5Z']
df_minmax = pd.DataFrame(index=list('xyz'),columns=range(2))
for i in list('xyz'):
c_max = df.max().max()
c_min = df.min().min()
df_minmax.loc[i] = np.array([c_min,c_max])
df_minmax = 3.3*df_minmax
df.columns = np.repeat(range(22),3)
N_tag = int(df.shape[1]/3) # nr of tags used (all)
N_trajectories = N_tag
t = np.linspace(0,data.Time[t_end],df.shape[0])
x_t = np.zeros(shape=(N_tag,df.shape[0],3))
for tag in range(22):
x_t[tag,:,:] = df[tag]
x_t = x_t[:, :, [0, 2, 1]]
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection='3d')
ax.axis('on')
#choose a different color for each trajectory
colors = plt.cm.jet(np.linspace(0, 1, N_trajectories))
# set up trajectory lines
lines = sum([ax.plot([], [], [], '-', c=c) for c in colors], [])
# set up points
pts = sum([ax.plot([], [], [], 'o', c=c) for c in colors], [])
#set up lines which create the stick figures
stick_defines = [
(0, 13),
(13, 14),
(14, 19),
(1, 5),
(2, 6),
(3, 7),
(4, 8),
(5, 9),
(6, 10),
(7, 11),
(8, 12),
(9, 15),
(10, 16),
(11, 17),
(12, 18),
(18, 21),
(15, 20)
]
stick_lines = [ax.plot([], [], [], 'k-')[0] for _ in stick_defines]
print(stick_lines)
ax.set_xlim3d([np.nanmin(x_t[:, :, 0]), np.nanmax(x_t[:, :, 0])])
ax.set_ylim3d([np.nanmin(x_t[:, :, 1])-400, np.nanmax(x_t[:, :, 1])+400])
ax.set_zlim3d([np.nanmin(x_t[:, :, 2]), np.nanmax(x_t[:, :, 2])])
ax.set_xlabel('X [mm]')
ax.set_ylabel('Y [mm]')
ax.set_zlabel('Z [mm]')
# set point-of-view: specified by (altitude degrees, azimuth degrees)
#ax.view_init(30, 25)
# initialization function: plot the background of each frame
def init():
for line, pt in zip(lines, pts):
line.set_data(np.array([]), np.array([]))
line.set_3d_properties(np.array([]))
pt.set_data(np.array([]),np.array([]))
pt.set_3d_properties(np.array([]))
return lines + pts
# animation function. This will be called sequentially with the frame number
def animate(i):
i = (5 * i) % x_t.shape[1]
for pt, xi in zip(pts, x_t):
x, y, z = xi[:i].T
pt.set_data(x[-1:], y[-1:])
pt.set_3d_properties(z[-1:])
for stick_line, (sp, ep) in zip(stick_lines, stick_defines):
stick_line._verts3d = x_t[[sp,ep], i, :].T.tolist()
#ax.view_init(30, 0.3 * i)
fig.canvas.draw()
return lines + pts + stick_lines
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=500, interval=30, blit=True, repeat=False)
# Save as mp4. This requires mplayer or ffmpeg to be installed
#anim.save('animation.mp4', progress_callback=lambda i, n: print(f'Saving frame {i} of {n}'))
plt.show()
anim.save('test.mp4', writer='ffmpeg')
This does not save animation. However,
anim.save('animation.mp4', progress_callback=lambda i, n: print(f'Saving frame {i} of {n}'))
Does save the animation but the rendering is not really sharp and quite blurry. How do I change that ?
The other points are:
How do I change all the scatter points to a single color ?eg dark grey or black
How do I change the index finger only to a different color? eg Index finger color red
best wishes,
PS: Attached is the animation

import numpy as np
import pandas as pd
import matplotlib
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.colors import cnames
from matplotlib import animation
#=============================================================================================
t_start = 2# start frame
t_end = 1711# end frame
data = pd.read_csv('Sub6_cylinder0009_index_flex_processed.csv') # only coordinate data
df = data.loc[t_start:t_end,'FT1X':'CMC5Z']
# Find max and min values for animation ranges
df_minmax = pd.DataFrame(index=list('xyz'),columns=range(2))
for i in list('xyz'):
c_max = df.max().max()
c_min = df.min().min()
df_minmax.loc[i] = np.array([c_min,c_max])
df_minmax = 3.3*df_minmax # increase by 30% to make animation look better
df.columns = np.repeat(range(22),3) # store cols like this for simplicity
print(df.columns)
N_tag = int(df.shape[1]/3) # nr of tags used (all)
N_trajectories = N_tag
t = np.linspace(0,data.Time[t_end],df.shape[0]) # pseudo time-vector for first walking activity
x_t = np.zeros(shape=(N_tag,df.shape[0],3)) # empty animation array (3D)
for tag in range(22):
# # store data in numpy 3D array: (tag,time-stamp,xyz-coordinates)
x_t[tag,:,:] = df[tag]
x_t = x_t[:, :, [0, 2, 1]]
fig = plt.figure()
ax = fig.add_axes([0, 0, 1, 1], projection='3d')
ax.axis('on')
#choose a different color for each trajectory
#colors1 = plt.cm.Reds(np.linspace(0, 1, N_trajectories))
#colors2 = plt.cm.gray(np.linspace(0, 1, N_trajectories))
# set up trajectory lines
#lines = sum([ax.plot([], [], [], '-', c=c) for c in colors1], [])
# set up points
#pts = sum([ax.plot([], [], [], 'o--', c=c) for c in colors2], [])
#pts = ax.plot([], [], [], 'o', c=colors2[0])
#pts = ax.plot([], [], [], 'o-', c=colors2[8])
#set up lines which create the stick figures
stick_defines = [
(0, 13), # Thumb Tip
(13, 14),
(14, 19),
(1, 5), #Index Tip
(2, 6), # Middle Tip
(3, 7), # Ring Tip
(4, 8), # Pinky Tip
(5, 9),
(6, 10),
(7, 11),
(8, 12),
(9, 15),
(10, 16),
(11, 17),
(12, 18),
(18, 21),
(15, 20)
# (22, 23),
# (23, 24),
# (22, 26),
# (25,26)
]
stick_defines1 = [
# (0, 13), # Thumb Tip
# (13, 14),
# (14, 19),
(1, 5), #Index Tip
# (2, 6), # Middle Tip
# (3, 7), # Ring Tip
# (4, 8), # Pinky Tip
(5, 9),
# (6, 10),
# (7, 11),
# (8, 12),
(9, 15),
# (10, 16),
# (11, 17),
# (12, 18),
# (18, 21),
(15, 20)
# (22, 23),
# (23, 24),
# (22, 26),
# (25,26)
]
stick_lines = [ax.plot([], [], [], 'ko--', alpha=0.5)[0] for _ in stick_defines]
stick_lines1 = [ax.plot([], [], [], 'ro-')[0] for _ in stick_defines1]
print(stick_lines)
ax.set_xlim3d([np.nanmin(x_t[:, :, 0]), np.nanmax(x_t[:, :, 0])])
ax.set_ylim3d([np.nanmin(x_t[:, :, 1])-400, np.nanmax(x_t[:, :, 1])+400])
ax.set_zlim3d([np.nanmin(x_t[:, :, 2]), np.nanmax(x_t[:, :, 2])])
ax.set_xlabel('X [mm]')
ax.set_ylabel('Y [mm]')
ax.set_zlabel('Z [mm]')
# set point-of-view: specified by (altitude degrees, azimuth degrees)
#ax.view_init(30, 25)
# initialization function: plot the background of each frame
# def init():
# for pt in pts:#zip(pts):
# # line.set_data(np.array([]), np.array([]))
# # line.set_3d_properties(np.array([]))
# pt.set_data(np.array([]), np.array([]))
# pt.set_3d_properties(np.array([]))
# return pts
# animation function. This will be called sequentially with the frame number
def animate(i):
# we'll step two time-steps per frame. This leads to nice results.
i = (5 * i) % x_t.shape[1]
pts = []
for pt, xi in zip(pts, x_t):
x, y, z = xi[:i].T # note ordering of points to line up with true exogenous registration (x,z,y)
pt.set_data(x[-1:], y[-1:])
pt.set_3d_properties(z[-1:])
for stick_line, (sp, ep) in zip(stick_lines, stick_defines):
stick_line._verts3d = x_t[[sp,ep], i, :].T.tolist()
for stick_line1, (sp, ep) in zip(stick_lines1, stick_defines1):
stick_line1._verts3d = x_t[[sp,ep], i, :].T.tolist()
#ax.view_init(30, 0.3 * i)
fig.canvas.draw()
return pts + stick_lines+stick_lines1
# instantiate the animator.
anim = animation.FuncAnimation(fig, animate, frames=500, interval=30, blit=True, repeat=False)
plt.show()
# Save as mp4. This requires mplayer or ffmpeg to be installed
anim.save('animation.mp4', progress_callback=lambda i, n: print(f'Saving frame {i} of {n}'))
#anim.save('test.mp4', writer='ffmpeg')
I have managed to answer my questions, however, the saving of the animation still remains a problem, the current version works but the video is blurry and is not particularly sharp. The last line of code which is commented out simply does not work for unknown reasons.
If you have a better way to have save the animation with better resolution please let me know.
Attached is the animation.
best wishes,

Related

Adding attribute (capacity) to node and edge in a graph created using dictionary

I have created 2 small graphs using python dictionaries and Networkx.
Now I want to assign and display an attribute (capacity) to the nodes and the edges. I failed in doing this when a graph was created using the dictionary. My code is:
#VNFGraph
Nf = {'VNF1', 'VNF2', 'VNF3', 'VNF4', 'VNF5'}
Lf = {('VNF1', 'VNF2'),('VNF2', 'VNF3'),('VNF3', 'VNF4'), ('VNF2', 'VNF5'),('VNF5', 'VNF3')}
# SERVERGraph
Ns = {'S1', 'S2', 'S3', 'S4', 'S5'}
Ls = {('S1', 'S2'),('S1', 'S4'),('S2', 'S4'),('S2', 'S3'),('S4', 'S3'),('S4', 'S5'),('S3', 'S5')}
graph1 = nx.DiGraph()
graph1.add_nodes_from(list(Nf))
graph1.add_edges_from(list(Lf))
graph2 = nx.Graph()
graph2.add_nodes_from(list(Ns))
graph2.add_edges_from(list(Ls))
graph2.add_edge("S1", "S2", weight=4.7)
pos1 = ({'VNF1': (0, 6), 'VNF2': (2, 6), 'VNF3': (4, 6), 'VNF4': (6, 6), 'VNF5': (3, 4)})
pos = ({'S1': (0, 1), 'S2': (2, 2), 'S3': (4, 2), 'S4': (3, 1), 'S5': (6, 1)})
fig1, ax1 = plt.subplots(figsize=(7, 7))
nx.draw_networkx_nodes(graph1, pos=pos1, ax=ax1, edgecolors='black', node_size=1100)
nx.draw_networkx_labels(graph1, pos=pos1, ax=ax1, labels=dict(zip(Nf, Nf)), font_size=7)
nx.draw_networkx_edges(graph1, pos=pos1, ax=ax1, node_size=900, arrowsize=25)
nx.draw_networkx_nodes(graph2, pos=pos, ax=ax1, edgecolors='black', node_size=800)
nx.draw_networkx_labels(graph2, pos=pos, ax=ax1, labels=dict(zip(Ns, Ns)), font_size=8)
nx.draw_networkx_edges(graph2, pos=pos, ax=ax1, node_size=900, arrowsize=25)
plt.axis('on')
plt.show()
I want to assign values (capacity attribute) to each node and link and display that too.
You need to pass the capacity to the nx.draw_commands as a list of values (which are in the same order as the nodes/edges of your graph). My usual approach is to write the attributes to the graph and then read them out and pass them to the draw commands:
import networkx as nx
import matplotlib.pyplot as plt
from random import randint
Nf = {'VNF1', 'VNF2', 'VNF3', 'VNF4', 'VNF5'}
Lf = {('VNF1', 'VNF2'),('VNF2', 'VNF3'),('VNF3', 'VNF4'), ('VNF2', 'VNF5'),('VNF5', 'VNF3')}
graph1 = nx.DiGraph()
graph1.add_nodes_from(list(Nf))
graph1.add_edges_from(list(Lf))
pos1 = ({'VNF1': (0, 6), 'VNF2': (2, 6), 'VNF3': (4, 6), 'VNF4': (6, 6), 'VNF5': (3, 4)})
'''
Create a dict of node and edge weights:
Dict has to have following form: {node_label: {attribute1: value1, attribute2: value2 ...}}
'''
node_weight = {n:{'weight':randint(100, 1000)} for n in graph1.nodes()}
edge_weight = {e:{'weight':randint(1, 5)} for e in graph1.edges()}
'''
Write attributes to graph
'''
nx.set_node_attributes(graph1, node_weight)
nx.set_edge_attributes(graph1, edge_weight)
'''
Get attributes from graph; returns dict: {node_label: value (of specified attribtue)}
In order to pass them to nx.draw you need a list, so unpack the values of the dict
This way you avoid messing up the order of the nodes
'''
node_size = list(nx.get_node_attributes(graph1, 'weight').values())
edge_size = list(nx.get_edge_attributes(graph1, 'weight').values())
fig1, ax1 = plt.subplots(figsize=(7, 7))
nx.draw_networkx_nodes(graph1, pos=pos1, ax=ax1, edgecolors='black', node_size=node_size)
nx.draw_networkx_labels(graph1, pos=pos1, ax=ax1, labels=dict(zip(Nf, Nf)), font_size=7)
nx.draw_networkx_edges(graph1, pos=pos1, ax=ax1, width = edge_size, arrowsize=25)
plt.axis('on')
plt.show()

How to separate images of graphs?

I need to draw these complete graphs that the edges of the separate graphs don't intersect intercept each other. I have tried to use coordinates in matplotlib but it didn't work. That is the code:
import networkx as nx
import matplotlib.pyplot as plt
G=nx.Graph()
G.add_edge("1", "2")
G.add_edge("2","3")
G.add_edge("3","4")
G.add_edge("1","3")
G.add_edge("1","4")
G.add_edge("2","4")
pos = {1: (0, 0), 2: (1, 1), 3: (0, 1) , 4: (1, 0)}
F=nx.Graph()
F.add_edge("5", "6")
F.add_edge("6","7")
F.add_edge("7","8")
F.add_edge("5","7")
F.add_edge("5","8")
F.add_edge("6","8")
pos = {5: (10, 10), 6: (11, 11), 7: (10, 11) , 8: (11, 10)}
E=nx.Graph()
E.add_edge("9", "10")
E.add_edge("10","11")
E.add_edge("9","11")
pos = nx.random_layout(E)
Y=nx.Graph()
Y.add_node("12")
pos = nx.random_layout(Y)
nx.draw(G, with_labels = True, node_color = 'white')
nx.draw(F, with_labels = True, node_color = 'white')
nx.draw(E, with_labels = True, node_color = 'white')
nx.draw(Y, with_labels = True, node_color = 'white')
plt.savefig('labels.png')
plt.show()
Result
You create the pos variable and overwrite it multiple times without using it at any point. Simply create multiple with non overlapping drawing areas:
import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
G.add_edge("1", "2")
G.add_edge("2", "3")
G.add_edge("3", "4")
G.add_edge("1", "3")
G.add_edge("1", "4")
G.add_edge("2", "4")
pos_G = {"1": (0, 0), "2": (1, 1), "3": (0, 1), "4": (1, 0)}
F = nx.Graph()
F.add_edge("5", "6")
F.add_edge("6", "7")
F.add_edge("7", "8")
F.add_edge("5", "7")
F.add_edge("5", "8")
F.add_edge("6", "8")
pos_F = {"5": (10, 10), "6": (11, 11), "7": (10, 11), "8": (11, 10)}
E = nx.Graph()
E.add_edge("9", "10")
E.add_edge("10", "11")
E.add_edge("9", "11")
pos_E = nx.random_layout(E)
pos_E = {node: pos + 3 for node, pos in pos_E.items()}
Y = nx.Graph()
Y.add_node("12")
pos_Y = nx.random_layout(Y)
pos_Y = {node: pos + 5 for node, pos in pos_Y.items()}
nx.draw(G, pos_G, with_labels=True, node_color='white')
nx.draw(F, pos_F, with_labels=True, node_color='white')
nx.draw(E, pos_E, with_labels=True, node_color='white')
nx.draw(Y, pos_Y, with_labels=True, node_color='white')
# plt.savefig('labels.png')
plt.show()
Result

How to calculate the euclidean distance between neighbors in networkx using x,y coordinates automatically and find the minimum spanning tree

So far I calculate the euclidean distance between the neighbors manually. But I need help on our to calculate the distance automatically in the code. My code so far is:
import networkx as nx
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
G = nx.Graph()
list_edges = [(0,1,3.8), (0,2,3.6), (1,3,2.5), (2,4,3.5), (3,5,4.6), (3,6,4.0), (3,7,2.8), (4,8,2.9),
(4,9,2.7), (4,10,4.1), (7,8,2.2), (5,6,3.1), (6,7,3.2), (8,9,3.6), (9,10,3.4)]
G.add_weighted_edges_from(list_edges)
G.add_node(0, pos = (8.5,10.5))
G.add_node(1, pos = (5,9))
G.add_node(2, pos = (11.5,8.5))
G.add_node(3, pos = (5,6.5))
G.add_node(4, pos = (11.5,5))
G.add_node(5, pos = (1.5,3.5))
G.add_node(6, pos = (4.5,2.5))
G.add_node(7, pos = (7,4.5))
G.add_node(8, pos = (9,3.5))
G.add_node(9, pos = (12.5,2.5))
G.add_node(10, pos = (15.5,4))
T = nx.minimum_spanning_tree(G, algorithm='kruskal')
#print(G.adj.items())
#print(T.edges())
#node_list = G.nodes()
#print(node_list)
#print(nx.get_node_attributes(G,'pos'))
node_pos=nx.get_node_attributes(G,'pos')
edge_weight=nx.get_edge_attributes(G,'weight')
red_edges = T.edges()
node_col = ['white']
# If the edge is in the shortest path set it to red, else set it to white color
edge_col = ['black' if not edge in red_edges else 'red' for edge in G.edges()]
# Draw the nodes
nx.draw_networkx(G, node_pos,node_color= node_col, node_size=450)
# Draw the node labels
nx.draw_networkx_labels(G, node_pos,node_color= node_col)
# Draw the edges
nx.draw_networkx_edges(G, node_pos,edge_color= edge_col)
# Draw the edge labels
nx.draw_networkx_edge_labels(G, node_pos,edge_color= edge_col, edge_labels=edge_weight)
# Remove the axis
plt.axis('off')
# Show the plot
plt.show()
Any help will be appreciated
You are probably looking for scipy.spatial.distance.pdist, which calculates all pairwise distances. If you need only few distances, you could also calculate them with scipy.spatial.distane.euclidean.
The following code reproduces your results without the requirement of given distances:
import networkx as nx
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial.distance import pdist, squareform
import seaborn as sns
sns.set()
G = nx.Graph()
# list_edges = [(0, 1, 3.8), (0, 2, 3.6), (1, 3, 2.5), (2, 4, 3.5), (3, 5, 4.6), (3, 6, 4.0), (3, 7, 2.8), (4, 8, 2.9),
# (4, 9, 2.7), (4, 10, 4.1), (7, 8, 2.2), (5, 6, 3.1), (6, 7, 3.2), (8, 9, 3.6), (9, 10, 3.4)]
# G.add_weighted_edges_from(list_edges)
list_unweighted_edges = [(0, 1), (0, 2), (1, 3), (2, 4), (3, 5), (3, 6), (3, 7), (4, 8),
(4, 9), (4, 10), (7, 8), (5, 6), (6, 7), (8, 9), (9, 10)]
G.add_node(0, pos=(8.5, 10.5))
G.add_node(1, pos=(5, 9))
G.add_node(2, pos=(11.5, 8.5))
G.add_node(3, pos=(5, 6.5))
G.add_node(4, pos=(11.5, 5))
G.add_node(5, pos=(1.5, 3.5))
G.add_node(6, pos=(4.5, 2.5))
G.add_node(7, pos=(7, 4.5))
G.add_node(8, pos=(9, 3.5))
G.add_node(9, pos=(12.5, 2.5))
G.add_node(10, pos=(15.5, 4))
position_array = []
for node in sorted(G):
position_array.append(G.nodes[node]["pos"])
print(position_array)
distances = squareform(pdist(np.array(position_array)))
for u, v in list_unweighted_edges:
G.add_edge(u, v, weight=np.round(distances[u][v],decimals=1))
T = nx.minimum_spanning_tree(G, algorithm='kruskal')
node_pos = nx.get_node_attributes(G, 'pos')
edge_weight = nx.get_edge_attributes(G, 'weight')
red_edges = T.edges()
node_col = ['white']
# If the edge is in the shortest path set it to red, else set it to white color
edge_col = ['black' if not edge in red_edges else 'red' for edge in G.edges()]
# Draw the nodes
nx.draw_networkx(G, node_pos, node_color=node_col, node_size=450)
# Draw the node labels
nx.draw_networkx_labels(G, node_pos, node_color=node_col)
# Draw the edges
nx.draw_networkx_edges(G, node_pos, edge_color=edge_col)
# Draw the edge labels
nx.draw_networkx_edge_labels(G, node_pos, edge_color=edge_col, edge_labels=edge_weight)
# Remove the axis
plt.axis('off')
# Show the plot
plt.show()

Matplotlib not showing point in PyCharm

Using Python 3 in PyCharm on Windows 10
I have a list of tuples that I need to plot. But the matplotlib is showing an empty graph:
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.use('TkAgg')
input = [(1, 6), (4, 15), (7, 7), (10, 13), (11, 6),
(11, 18), (11, 21), (12, 10), (15, 18),
(16, 6), (18, 3), (18, 12), (19, 15), (22, 19)]
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
input_points = []
for array_x, array_y in input:
input_points.append(Point(array_x, array_y))
plt.plot(array_x, array_y)
plt.show()
How to get the points to show up and plotted in the graph?
To draw lines, plt.plot needs a list (or numpy array) of x-positions and a list of y-positions. The documentation lists the different options to draw markers and/or lines.
List comprehension is a handy way to extract x or y positions from a list of xy-coordinates.
import matplotlib.pyplot as plt
input_points = [(1, 6), (4, 15), (7, 7), (10, 13), (11, 6),
(11, 18), (11, 21), (12, 10), (15, 18),
(16, 6), (18, 3), (18, 12), (19, 15), (22, 19)]
array_x = [x for x, y in input_points]
array_y = [y for x, y in input_points]
plt.plot(array_x, array_y, marker='o', color='crimson', linestyle='-')
plt.show()

How to annotated labels to a 3D matplotlib scatter plot?

I have run a sklearn - Principal Component Analysis on my data with 3 principal components (PC1, PC2, PC3). The data looks like this (it's a pandas DataFrame):
Here is the code for plotting the principle components:
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
%matplotlib
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_title('3D Scatter Plot')
ax.set_xlabel('PC1')
ax.set_ylabel('PC2')
ax.set_zlabel('PC3')
ax.view_init(elev=12, azim=40) # elevation and angle
ax.dist=10 # distance
ax.scatter(
data_df_3dx['PC1'], data_df_3dx['PC2'], data_df_3dx['PC3'], # data
#color='purple', # marker colour
#marker='o', # marker shape
s=60 # marker size
)
plt.show()
My problem, how do I add labels (like 'GER, medium') to the points? Hope someone can help me :)
In the following posts [1], [2] the plotting of 3D arrows in matplotlib is discussed.
Similarly Annotation3D class (inherited from Annotation) can be created:
from mpl_toolkits.mplot3d.proj3d import proj_transform
from matplotlib.text import Annotation
class Annotation3D(Annotation):
'''Annotate the point xyz with text s'''
def __init__(self, s, xyz, *args, **kwargs):
Annotation.__init__(self,s, xy=(0,0), *args, **kwargs)
self._verts3d = xyz
def draw(self, renderer):
xs3d, ys3d, zs3d = self._verts3d
xs, ys, zs = proj_transform(xs3d, ys3d, zs3d, renderer.M)
self.xy=(xs,ys)
Annotation.draw(self, renderer)
Further, we can define the annotate3D() function:
def annotate3D(ax, s, *args, **kwargs):
'''add anotation text s to to Axes3d ax'''
tag = Annotation3D(s, *args, **kwargs)
ax.add_artist(tag)
Using this function annotation tags can be added to Axes3d as in example bellow:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
from mpl_toolkits.mplot3d.art3d import Line3DCollection
# data: coordinates of nodes and links
xn = [1.1, 1.9, 0.1, 0.3, 1.6, 0.8, 2.3, 1.2, 1.7, 1.0, -0.7, 0.1, 0.1, -0.9, 0.1, -0.1, 2.1, 2.7, 2.6, 2.0]
yn = [-1.2, -2.0, -1.2, -0.7, -0.4, -2.2, -1.0, -1.3, -1.5, -2.1, -0.7, -0.3, 0.7, -0.0, -0.3, 0.7, 0.7, 0.3, 0.8, 1.2]
zn = [-1.6, -1.5, -1.3, -2.0, -2.4, -2.1, -1.8, -2.8, -0.5, -0.8, -0.4, -1.1, -1.8, -1.5, 0.1, -0.6, 0.2, -0.1, -0.8, -0.4]
group = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3]
edges = [(1, 0), (2, 0), (3, 0), (3, 2), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0), (9, 0), (11, 10), (11, 3), (11, 2), (11, 0), (12, 11), (13, 11), (14, 11), (15, 11), (17, 16), (18, 16), (18, 17), (19, 16), (19, 17), (19, 18)]
xyzn = zip(xn, yn, zn)
segments = [(xyzn[s], xyzn[t]) for s, t in edges]
# create figure
fig = plt.figure(dpi=60)
ax = fig.gca(projection='3d')
ax.set_axis_off()
# plot vertices
ax.scatter(xn,yn,zn, marker='o', c = group, s = 64)
# plot edges
edge_col = Line3DCollection(segments, lw=0.2)
ax.add_collection3d(edge_col)
# add vertices annotation.
for j, xyz_ in enumerate(xyzn):
annotate3D(ax, s=str(j), xyz=xyz_, fontsize=10, xytext=(-3,3),
textcoords='offset points', ha='right',va='bottom')
plt.show()
One way would be to plot each point individually inside of a for loop, that way you know the coordinates of each point and can add text to it.
for i in range(len(data_df_3dx)):
x, y, z = data_df_3dx.iloc[i]['PC1'], data_df_3dx.iloc[i]['PC2'], data_df_3dx.iloc[i]['PC3']
ax.scatter(x, y, z)
#now that you have the coordinates you can apply whatever text you need. I'm
#assuming you want the index, but you could also pass a column name if needed
ax.text(x, y, z, '{0}'.format(data_df_3dx.index[i]), size=5)

Resources