In the past, I have written about the potential for 3D rendering and virtual reality to improve multivariate data visualization. I my new book, The Blender Python API, I have recorded and explained some of the methods I use for multivariate data visualization in Blender.
This article is a taste of what can be found in that book, and an explanation of how I rendered the example images in the aforementioned article.
Reading in the Data
For this example, we will use Fisher’s Iris data. The copy we will use can be downloaded here: Fisher’s Iris CSV. This is the hardest part of the whole script, because we do not have access to the pandas library by default in Blender Python. So, we will read in our data via a list of lists, making sure to store the header in a separate list of equal dimension.
import bpy import csv import urllib.request # Download the data url_str = 'http://blender.chrisconlan.com/iris.csv' iris_csv = urllib.request.urlopen(url_str) # Read it into a csv iterator iris_ob = csv.reader(iris_csv.read().decode('utf-8').splitlines()) iris_header =  iris_data =  # Loop through the csv file, recording the header and the data for v in iris_ob: if not iris_header: iris_header = v else: v = [float(v), float(v), float(v), float(v), str(v)] iris_data.append(v)
We place this code at the beginning of the following scripts to load in the data before plotting. The data is downloaded at run-time via the urllib library, so make sure you have internet connection.
Overview of Iris Data
The Iris data has 5 columns and 150 rows. The first 4 columns are numeric data that describe the shape of the flower. The last column is a string variable that specifies the Iris’s subspecies. This subspecies can be Setosa, Versicolor, or Virginica. This example will attempt to plot all five variables in the same 3D rendering, starting with 3 dimensions and moving up to 5 dimensions.
Plotting 3 Dimensions
The most natural way to plot 3D data in 3D is to map one numeric variable to each axis, X, Y, and Z. We will create circles, scale them down for easy viewing, then place them in the 3D space according to the first three numeric rows of the Iris data. Remember to paste this after the above script in the same text file to run it correctly.
for i in range(len(iris_data)): # Create a sphere at the origin bpy.ops.mesh.primitive_uv_sphere_add(location=(0,0,0)) # Name it 'row-i' bpy.context.object.name = 'row-' + str(i) v = iris_data[i] # Resize with an an arbitrary but more aesthetic value bpy.ops.transform.resize(value=(0.15, 0.15, 0.15)) # Move the sphere to the correct position in 3D bpy.ops.transform.translate(value=(v, v, v))
Plotting 4 Dimensions
To plot the fourth numeric column of the Iris data, we can control some aspect of the sphere that does not alter its position. We will choose the size of the sphere, in this case. We will do the same process as above, except we will make the size of the spheres proportional to the square root of the numeric column. Paste the following after the first script to both position and resize the sphere according to the data.
scale_factor = 0.1 for i in range(len(iris_data)): bpy.ops.mesh.primitive_uv_sphere_add(location=(0,0,0)) bpy.context.object.name = 'row-' + str(i) v = iris_data[i] # Size is a triplet of the scaled sqrt of fourth column bpy.ops.transform.resize(value=(v**0.5*scale_factor,)*3) bpy.ops.transform.translate(value=(v, v, v))
Plotting 5 Dimensions
Finally, we will alter the shape of the data point to represent the type of flower. We have chosen spheres, cubes, and cones to represent Setosa, Versicolor, and Virginica flowers, respectively.
scale_factor = 0.1 for i in range(len(iris_data)): v = iris_data[i] if v == 'setosa': bpy.ops.mesh.primitive_uv_sphere_add(location=(0,0,0)) if v == 'versicolor': bpy.ops.mesh.primitive_cube_add(location=(0,0,0)) if v == 'virginica': bpy.ops.mesh.primitive_cone_add(location=(0,0,0)) bpy.context.object.name = 'row-' + str(i) bpy.ops.transform.resize(value=(v**0.5a*scale_factor,)*3) bpy.ops.transform.translate(value=(v, v, v))
The final script can plot 5 dimensions of multivariate data in less than 40 lines of code. The Blender Python API is just cool. I highly recommend that anyone interested run these examples all the way through. While I can screenshot the result for this article, it feels extremely cool to use Blender’s mouse controls to fly in and out of the data cloud. Personally, it helped me understand this deceptively simple data set that, until now, I was unable to wrap my head around.
For an in depth explanation and access to helpful Blender Python modules, check out Chapter 2 of The Blender Python API (Apress).