How to Create Dynamic 3D Scatter Plots with Plotly | by Soner Yıldırım | Towards Data Science

Visualizations stick in minds much more than plain text or numbers. Informative power of visualizations is hard to achieve with plain text. Thus, visualizations are widely used in the field of data science. The informative power of visualization depends on its structure. For instance, a 3D dynamic plot can tell much more than a 2D plot. As the structure gets more complex, we can represent more information with a visualization. Before applying a model to the data, it is highly important to explore and understand the structures within the raw data. Visualizations are great tools that expedite exploring the data and help build a robust data analysis process.
In this post, we will create dynamic 3D scatter plots and compare them to 2D scatter plots. We will use Plotly Python (plotly.py) which is an open-source plotting library built on plotly javascript (plotly.js). One of the things I like about plotly.py is that it offers a high-level API (plotly express) and a low level API (graph objects) to create visualizations. With plotly express, we can create a nice plot with very few lines of code. On the other hand, we need to write more code with graph objects but have more control over what we create.
Let’s first create a synthetic dataset using numpy and pandas. We start by importing the libraries.
import numpy as npimport pandas as pdimport plotly.express as px
We create a dataframe that contains 3 different numerial features on some observations that belong to 2 different categories. We use numpy arrays for numerical features and python lists to create category column.
Numerical features:
x = np.random.random(size=60)*7 + 1y = np.random.random(size=60)*7 + 1z = np.random.random(size=60)z[-30:] = z[-30:]*5 + 2
Categorical feature:
categories = "A "*30 + "B "*30categories = categories.split(" ")categories.pop(60)
The first create a string that contains 30 As and 30 Bs (with spaces in between) and then split the string on spaces to create a list of 30 items. Since there is a space at the end of the string, the last element of the list is a space (‘ ‘). We remove it using pop method.
Using these 4 arrays, we can create a pandas dataframe.
df = pd.DataFrame({'cat':categories, 'col_x':x, 'col_y':y, 'col_z':z})df.head()
Let’s create a 2D scatter plots based on col_x and col_y.
fig = px.scatter(df, x='col_x', y='col_y', color='cat',                 width=700, height=500,                 title="2D Scatter Plot")fig.show()
We pass the name of the dataframe, the columns to be used as x-axis and y-axis. The optional color parameter represents the category of datapoints.
As we can see, there is not a clear distinction between datapoints in categories A and B. In some cases, adding one more feature to the visualization tells a lot. We created the dataframe in such a way that “col_z” carries informative power.
Let’s now create a 3D scatter plot using all of the features.
fig = px.scatter_3d(df, x='col_x', y='col_y', z='col_z',                    color='cat',                    title="3D Scatter Plot")fig.show()
The values in col_z clearly distinguish categories A and B. We can, of course, use col_z on x-axis or y-axis and achieve a similar result with 2D scatter plot. However, there are some cases in which all columns carry valuable information that we need.
Let’s build a more complex dataframe that each feature matters for classification.
Features:
x = np.random.random(size=100)*5x[-30:] = x[-30:] + 6x = x + np.random.random(size=100)*8y = np.random.random(size=100)*5y[:30] = y[:30]*4 + 8y = y + np.random.random(size=100)*3y[-30:] = y[-30:] + 4z = np.random.random(size=100)*5z[30:70] = z[30:70] - np.arange(1,41) / 40z = z + np.random.random(size=100)*5categories = "A "*30 + "B "*40 + "C "*30categories = categories.split(" ")categories.pop(100)
Dataframe:
df = pd.DataFrame({'cat':categories, 'col_x':x, 'col_y':y, 'col_z':x})df.head()
The scatter plot based on new features:
Another important parameter of px.scatter_3d is the size. The points in the plots we have created so far have same size. However, we can also assign a feature to the size parameter. Then the sizes of the data points change according to the values in that feature.
We assigned a categorical variable to color parameter so the data points are represented with a separate color. Plotly provides the option to use a numerical feature for color parameter as well. In this case, the colors of points change based on a scale.
Let’s create one more 3D scatter plot using the size parameter. We will use the famous “iris” dataset which is available as a built-in dataset in plotly library.
df_iris = px.data.iris()df_iris.head()
fig = px.scatter_3d(df_iris, x='sepal_length', y='sepal_width', z='petal_width',  color='petal_length',size='petal_length', size_max=20)fig.show()
There is one more parameter that adds informative power to dynamic 3D scatter plots which is symbol. It only takes a categorical variable and the symbol that represents the datapoints change according to the category. For instance, category A can be represented with squares whereas category B with circles.
A dynamic 3D plots carry lots of information. If “one picture is worth a thousand words”, these 3D dynamic plots have the potential to hit up to a million words:)
Thanks for reading. Please let me know if you have any feedback.