How to make 4d plot with matplotlib using arbitrary data

This question is related to this one .

I would like to know how to apply the proposed solution to a dataset (4 columns), for example:

0.1 0 0.1 2.0 0.1 0 1.1 -0.498121712998 0.1 0 2.1 -0.49973005075 0.1 0 3.1 -0.499916082038 0.1 0 4.1 -0.499963726586 0.1 1 0.1 -0.0181405895692 0.1 1 1.1 -0.490774988618 0.1 1 2.1 -0.498653742846 0.1 1 3.1 -0.499580747953 0.1 1 4.1 -0.499818696063 0.1 2 0.1 -0.0107079119572 0.1 2 1.1 -0.483641823093 0.1 2 2.1 -0.497582061233 0.1 2 3.1 -0.499245863438 0.1 2 4.1 -0.499673749657 0.1 3 0.1 -0.0075248589089 0.1 3 1.1 -0.476713038166 0.1 3 2.1 -0.49651497615 0.1 3 3.1 -0.498911427589 0.1 3 4.1 -0.499528887295 0.1 4 0.1 -0.00579180003048 0.1 4 1.1 -0.469979974092 0.1 4 2.1 -0.495452458086 0.1 4 3.1 -0.498577439505 0.1 4 4.1 -0.499384108904 1.1 0 0.1 302.0 1.1 0 1.1 -0.272727272727 1.1 0 2.1 -0.467336140806 1.1 0 3.1 -0.489845926622 1.1 0 4.1 -0.495610916847 1.1 1 0.1 -0.000154915998165 1.1 1 1.1 -0.148803329865 1.1 1 2.1 -0.375881358454 1.1 1 3.1 -0.453749548548 1.1 1 4.1 -0.478942841849 1.1 2 0.1 -9.03765566114e-05 1.1 2 1.1 -0.0972702806613 1.1 2 2.1 -0.314291859842 1.1 2 3.1 -0.422606253083 1.1 2 4.1 -0.463359353084 1.1 3 0.1 -6.31234088628e-05 1.1 3 1.1 -0.0720095219203 1.1 3 2.1 -0.270015786897 1.1 3 3.1 -0.395462300716 1.1 3 4.1 -0.44875793248 1.1 4 0.1 -4.84199181874e-05 1.1 4 1.1 -0.0571187054704 1.1 4 2.1 -0.236660992042 1.1 4 3.1 -0.371593983211 1.1 4 4.1 -0.4350485869 2.1 0 0.1 1102.0 2.1 0 1.1 0.328324567994 2.1 0 2.1 -0.380952380952 2.1 0 3.1 -0.462992178846 2.1 0 4.1 -0.48400342421 2.1 1 0.1 -4.25137933034e-05 2.1 1 1.1 -0.0513190921508 2.1 1 2.1 -0.224866151101 2.1 1 3.1 -0.363752470126 2.1 1 4.1 -0.430700436658 2.1 2 0.1 -2.48003822279e-05 2.1 2 1.1 -0.0310025255124 2.1 2 2.1 -0.158022037087 2.1 2 3.1 -0.29944612818 2.1 2 4.1 -0.387965424205 2.1 3 0.1 -1.73211484062e-05 2.1 3 1.1 -0.0220466245862 2.1 3 2.1 -0.12162780064 2.1 3 3.1 -0.254424041889 2.1 3 4.1 -0.35294082311 2.1 4 0.1 -1.32862131387e-05 2.1 4 1.1 -0.0170828002197 2.1 4 2.1 -0.0988138417802 2.1 4 3.1 -0.221154587294 2.1 4 4.1 -0.323713596671 3.1 0 0.1 2402.0 3.1 0 1.1 1.30503380917 3.1 0 2.1 -0.240578771191 3.1 0 3.1 -0.41935483871 3.1 0 4.1 -0.465141248676 3.1 1 0.1 -1.95102493785e-05 3.1 1 1.1 -0.0248114638773 3.1 1 2.1 -0.135153019304 3.1 1 3.1 -0.274125336409 3.1 1 4.1 -0.36965644171 3.1 2 0.1 -1.13811197906e-05 3.1 2 1.1 -0.0147116366819 3.1 2 2.1 -0.0872950700627 3.1 2 3.1 -0.202935925412 3.1 2 4.1 -0.306612285308 3.1 3 0.1 -7.94877050259e-06 3.1 3 1.1 -0.0103624783432 3.1 3 2.1 -0.0642253568271 3.1 3 3.1 -0.160970897235 3.1 3 4.1 -0.261906474418 3.1 4 0.1 -6.09709039262e-06 3.1 4 1.1 -0.00798626913355 3.1 4 2.1 -0.0507564081263 3.1 4 3.1 -0.133349565782 3.1 4 4.1 -0.228563754423 4.1 0 0.1 4202.0 4.1 0 1.1 2.65740045079 4.1 0 2.1 -0.0462153115214 4.1 0 3.1 -0.358933906213 4.1 0 4.1 -0.439024390244 4.1 1 0.1 -1.11538537794e-05 4.1 1 1.1 -0.0144619860317 4.1 1 2.1 -0.0868190343718 4.1 1 3.1 -0.203767982755 4.1 1 4.1 -0.308519215265 4.1 2 0.1 -6.50646078271e-06 4.1 2 1.1 -0.0085156584289 4.1 2 2.1 -0.0538784714494 4.1 2 3.1 -0.140215240068 4.1 2 4.1 -0.23746380125 4.1 3 0.1 -4.54421180079e-06 4.1 3 1.1 -0.00597669061814 4.1 3 2.1 -0.038839789599 4.1 3 3.1 -0.106675396816 4.1 3 4.1 -0.192922262523 4.1 4 0.1 -3.48562423225e-06 4.1 4 1.1 -0.00459693165308 4.1 4 2.1 -0.0303305231375 4.1 4 3.1 -0.0860368842133 4.1 4 4.1 -0.162420599686 

The solution to the original problem:

 # Python-matplotlib Commands from mpl_toolkits.mplot3d import Axes3D from matplotlib import cm import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.gca(projection='3d') X = np.arange(-5, 5, .25) Y = np.arange(-5, 5, .25) X, Y = np.meshgrid(X, Y) R = np.sqrt(X**2 + Y**2) Z = np.sin(R) Gx, Gy = np.gradient(Z) # gradients with respect to x and y G = (Gx**2+Gy**2)**.5 # gradient magnitude N = G/G.max() # normalize 0..1 surf = ax.plot_surface( X, Y, Z, rstride=1, cstride=1, facecolors=cm.jet(N), linewidth=0, antialiased=False, shade=False) plt.show() 

As far as I can tell, and this applies to all matplotlib-demos, the variables X, Y and Z are well prepared. In practical cases, this is not always the case.

Ideas how to reuse this solution with arbitrary data?

+17
source share
3 answers

Great question, Tengis, all mathematicians like to demonstrate vivid surface graphics with these functions, not paying attention to real-world data. The code example you use uses gradients, since variable relationships are modeled using functions. For this example, I will generate random data using the standard normal distribution.

In any case, here's how you can quickly build 4D random (arbitrary) data with the first three variables on the axis, and the fourth with color:

 from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(111, projection='3d') x = np.random.standard_normal(100) y = np.random.standard_normal(100) z = np.random.standard_normal(100) c = np.random.standard_normal(100) img = ax.scatter(x, y, z, c=c, cmap=plt.hot()) fig.colorbar(img) plt.show() 

Note. A heat map with a hot color scheme (yellow to red) was used for the 4th dimension

Result:

] 1

+34
source

I know that the question is very old, but I would like to introduce this alternative, where instead of using the "scatter plot" we have a three-dimensional surface diagram, where the colors are based on the 4th dimension. Personally, I don’t see the spatial relationship in the case of a “scatter plot,” and therefore using a three-dimensional surface helps me understand the plot more easily.

The basic idea is similar to the accepted answer, but we have a three-dimensional surface graph that allows us to visually better see the distance between points. The following code is mainly based on the answer asked to this question .

 import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import matplotlib.tri as mtri # The values ​​related to each point. This can be a "Dataframe pandas" # for example where each column is linked to a variable <-> 1 dimension. # The idea is that each line = 1 pt in 4D. do_random_pt_example = True; index_x = 0; index_y = 1; index_z = 2; index_c = 3; list_name_variables = ['x', 'y', 'z', 'c']; name_color_map = 'seismic'; if do_random_pt_example: number_of_points = 200; x = np.random.rand(number_of_points); y = np.random.rand(number_of_points); z = np.random.rand(number_of_points); c = np.random.rand(number_of_points); else: # Example where we have a "Pandas Dataframe" where each line = 1 pt in 4D. # We assume here that the "data frame" "df" has already been loaded before. x = df[list_name_variables[index_x]]; y = df[list_name_variables[index_y]]; z = df[list_name_variables[index_z]]; c = df[list_name_variables[index_c]]; #end #----- # We create triangles that join 3 pt at a time and where their colors will be # determined by the values ​​of their 4th dimension. Each triangle contains 3 # indexes corresponding to the line number of the points to be grouped. # Therefore, different methods can be used to define the value that # will represent the 3 grouped points and I put some examples. triangles = mtri.Triangulation(x, y).triangles; choice_calcuation_colors = 1; if choice_calcuation_colors == 1: # Mean of the "c" values of the 3 pt of the triangle colors = np.mean( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0); elif choice_calcuation_colors == 2: # Mediane of the "c" values of the 3 pt of the triangle colors = np.median( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0); elif choice_calcuation_colors == 3: # Max of the "c" values of the 3 pt of the triangle colors = np.max( [c[triangles[:,0]], c[triangles[:,1]], c[triangles[:,2]]], axis = 0); #end #---------- # Displays the 4D graphic. fig = plt.figure(); ax = fig.gca(projection='3d'); triang = mtri.Triangulation(x, y, triangles); surf = ax.plot_trisurf(triang, z, cmap = name_color_map, shade=False, linewidth=0.2); surf.set_array(colors); surf.autoscale(); #Add a color bar with a title to explain which variable is represented by the color. cbar = fig.colorbar(surf, shrink=0.5, aspect=5); cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_ylabel(list_name_variables[index_c], rotation = 270); # Add titles to the axes and a title in the figure. ax.set_xlabel(list_name_variables[index_x]); ax.set_ylabel(list_name_variables[index_y]); ax.set_zlabel(list_name_variables[index_z]); plt.title('%s in function of %s, %s and %s' % (list_name_variables[index_c], list_name_variables[index_x], list_name_variables[index_y], list_name_variables[index_z]) ); plt.show(); 

Example of result with random values

Another solution for the case when we absolutely want to have the initial values ​​of the 4th dimension for each point is simply to use a “scatter plot” in combination with a three-dimensional surface diagram, which simply links them to help you see the distances between them.

 name_color_map_surface = 'Greens'; # Colormap for the 3D surface only. fig = plt.figure(); ax = fig.add_subplot(111, projection='3d'); ax.set_xlabel(list_name_variables[index_x]); ax.set_ylabel(list_name_variables[index_y]); ax.set_zlabel(list_name_variables[index_z]); plt.title('%s in fcn of %s, %s and %s' % (list_name_variables[index_c], list_name_variables[index_x], list_name_variables[index_y], list_name_variables[index_z]) ); # In this case, we will have 2 color bars: one for the surface and another for # the "scatter plot". # For example, we can place the second color bar under or to the left of the figure. choice_pos_colorbar = 2; #The scatter plot. img = ax.scatter(x, y, z, c = c, cmap = name_color_map); cbar = fig.colorbar(img, shrink=0.5, aspect=5); # Default location is at the 'right' of the figure. cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_ylabel(list_name_variables[index_c], rotation = 270); # The 3D surface that serves only to connect the points to help visualize # the distances that separates them. # The "alpha" is used to have some transparency in the surface. surf = ax.plot_trisurf(x, y, z, cmap = name_color_map_surface, linewidth = 0.2, alpha = 0.25); # The second color bar will be placed at the left of the figure. if choice_pos_colorbar == 1: #I am trying here to have the two color bars with the same size even if it #is currently set manually. cbaxes = fig.add_axes([1-0.78375-0.1, 0.3025, 0.0393823, 0.385]); # Case without tigh layout. #cbaxes = fig.add_axes([1-0.844805-0.1, 0.25942, 0.0492187, 0.481161]); # Case with tigh layout. cbar = plt.colorbar(surf, cax = cbaxes, shrink=0.5, aspect=5); cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_ylabel(list_name_variables[index_z], rotation = 90); # The second color bar will be placed under the figure. elif choice_pos_colorbar == 2: cbar = fig.colorbar(surf, shrink=0.75, aspect=20,pad = 0.05, orientation = 'horizontal'); cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_xlabel(list_name_variables[index_z], rotation = 0); #end plt.show(); 

Sample result for this second method with random values

Finally, it is also possible to use "plot_surface", where we define the color that will be used for each face. In this case, when we have 1 vector of values ​​per dimension, the problem is that we have to interpolate the values ​​to get 2D meshes. In case of interpolation of the 4th dimension, it will be determined only in accordance with XY and Z will not be taken into account. As a result, colors represent C (x, y) instead of C (x, y, z). The following code is mainly based on the following answers: plot_surface with a one-dimensional vector for each dimension ; plot_surface with the selected color for each surface . Please note that, compared to previous solutions, the calculation is rather complicated, and the display may take some time.

 import matplotlib from scipy.interpolate import griddata # XY are transformed into 2D grids. It like a form of interpolation x1 = np.linspace(x.min(), x.max(), len(np.unique(x))); y1 = np.linspace(y.min(), y.max(), len(np.unique(y))); x2, y2 = np.meshgrid(x1, y1); # Interpolation of Z: old XY to the new XY grid. # Note: Sometimes values ​​can be < z.min and so it may be better to set # the values too low to the true minimum value. z2 = griddata( (x, y), z, (x2, y2), method='cubic', fill_value = 0); z2[z2 < z.min()] = z.min(); # Interpolation of C: old XY on the new XY grid (as we did for Z) # The only problem is the fact that the interpolation of C does not take # into account Z and that, consequently, the representation is less # valid compared to the previous solutions. c2 = griddata( (x, y), c, (x2, y2), method='cubic', fill_value = 0); c2[c2 < c.min()] = c.min(); #-------- color_dimension = c2; # It must be in 2D - as for "X, Y, Z". minn, maxx = color_dimension.min(), color_dimension.max(); norm = matplotlib.colors.Normalize(minn, maxx); m = plt.cm.ScalarMappable(norm=norm, cmap = name_color_map); m.set_array([]); fcolors = m.to_rgba(color_dimension); # At this time, XYZC are all 2D and we can use "plot_surface". fig = plt.figure(); ax = fig.gca(projection='3d'); surf = ax.plot_surface(x2, y2, z2, facecolors = fcolors, linewidth=0, rstride=1, cstride=1, antialiased=False); cbar = fig.colorbar(m, shrink=0.5, aspect=5); cbar.ax.get_yaxis().labelpad = 15; cbar.ax.set_ylabel(list_name_variables[index_c], rotation = 270); ax.set_xlabel(list_name_variables[index_x]); ax.set_ylabel(list_name_variables[index_y]); ax.set_zlabel(list_name_variables[index_z]); plt.title('%s in fcn of %s, %s and %s' % (list_name_variables[index_c], list_name_variables[index_x], list_name_variables[index_y], list_name_variables[index_z]) ); plt.show(); 

Example of result with randoms values ​​for this third solution

+1
source

One possibility is to use a color space like RGBA or HSVA, they are 4-dimensional, but displaying alpha transparency can be a problem.

Another possibility is a dynamic plot with a slider. One of the sizes will be presented by a slider.

I am not sure if this is what you are asking for.

0
source

All Articles