[Tut] Matplotlib 3D Plot – A Helpful Illustrated Guide - Printable Version +- Sick Gaming (https://www.sickgaming.net) +-- Forum: Programming (https://www.sickgaming.net/forum-76.html) +--- Forum: Python (https://www.sickgaming.net/forum-83.html) +--- Thread: [Tut] Matplotlib 3D Plot – A Helpful Illustrated Guide (/thread-94396.html) |
[Tut] Matplotlib 3D Plot – A Helpful Illustrated Guide - xSicKxBot - 04-03-2020 Matplotlib 3D Plot – A Helpful Illustrated Guide <div><p>Are you tired with the same old 2D plots? Do you want to take your plots to the next level? Well look no further, it’s time to learn how to make 3D plots in <a href="https://matplotlib.org/" target="_blank" rel="noreferrer noopener">matplotlib</a>.</p> <figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-4-3 wp-has-aspect-ratio"> <div class="wp-block-embed__wrapper"> <div class="ast-oembed-container"><iframe title="Matplotlib 3D Plot - A Helpful Illustrated Guide" width="1333" height="1000" src="https://www.youtube.com/embed/7e3BGIXwQvo?feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></div> </div> </figure> <p>In addition to <code>import matplotlib.pyplot as plt</code> and calling <code>plt.show()</code>, to create a 3D plot in matplotlib, you need to:</p> <ol> <li>Import the <code>Axes3D</code> object</li> <li>Initialize your <code>Figure</code> and <code>Axes3D</code> objects</li> <li>Get some 3D data</li> <li>Plot it using <code>Axes</code> notation and standard function calls</li> </ol> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Standard import import matplotlib.pyplot as plt # Import 3D Axes from mpl_toolkits.mplot3d import axes3d # Set up Figure and 3D Axes fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # Get some 3D data X = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] Y = [2, 5, 8, 2, 10, 1, 10, 5, 7, 8] Z = [6, 3, 9, 6, 3, 2, 3, 10, 2, 4] # Plot using Axes notation and standard function calls ax.plot(X, Y, Z) plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img0.png" alt=""/></figure> <p>Awesome! You’ve just created your first 3D plot! Don’t worry if that was a bit fast, let’s dive into a more detailed example.</p> <p>Try it yourself with our interactive Python shell. Just execute the code and look at the generated “plot.png” file:</p> <p> <iframe height="800px" width="100%" src="https://repl.it/@finxter/matplotlib3d?lite=true" scrolling="no" frameborder="no" allowtransparency="true" allowfullscreen="true" sandbox="allow-forms allow-pointer-lock allow-popups allow-same-origin allow-scripts allow-modals"></iframe> </p> <p><strong>Related article:</strong></p> <ul> <li><a href="https://blog.finxter.com/start-learning-python/" target="_blank" rel="noreferrer noopener">How to start learning Python</a></li> </ul> <h2>Matplotlib 3D Plot Example</h2> <p>If you are used to plotting with <code>Figure</code> and <code>Axes</code> notation, making 3D plots in matplotlib is almost identical to creating 2D ones. If you are not comfortable with <code>Figure</code> and <code>Axes</code> plotting notation, check out <a href="https://blog.finxter.com/matplotlib-subplots/#Matplotlib_Figures_and_Axes">this</a> article to help you.</p> <p>Besides the standard <code>import matplotlib.pyplot as plt</code>, you must also<code>from mpl_toolkits.mplot3d import axes3d</code>. This imports a 3D <code>Axes</code> object on which a) you can plot 3D data and b) you will make all your plot calls with respect to.</p> <p>You set up your <code>Figure</code> in the standard way</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fig = plt.figure() </pre> <p>And add a subplots to that figure using the standard <code>fig.add_subplot()</code> method. If you just want a single <code>Axes</code>, pass <code>111</code> to indicate it’s 1 row, 1 column and you are selecting the 1st one. Then you need to pass <code>projection='3d'</code> which tells matplotlib it is a 3D plot.</p> <p>From now on everything is (almost) the same as 2D plotting. All the functions you know and love such as <code>ax.plot()</code> and <code>ax.scatter()</code> accept the same keyword arguments but they now also accept three positional arguments – <code>X</code>,<code>Y</code> and <code>Z</code>.</p> <p>In some ways 3D plots are more natural for us to work with since we live in a 3D world. On the other hand, they are more complicated since we are so used to 2D plots. One amazing feature of Jupyter Notebooks is the magic command <code>%matplotlib notebook</code> which, if ran at the top of your notebook, draws all your plots in an interactive window. You can change the orientation by clicking and dragging (right click and drag to zoom in) which can really help to understand your data.</p> <p>As this is a static blog post, all of my plots will be static but I encourage you to play around in your own Jupyter or IPython environment.</p> <p><strong>Related article:</strong></p> <ul> <li><a href="https://blog.finxter.com/how-to-check-your-python-version/" target="_blank" rel="noreferrer noopener">Check your Python version in Jupyter Notebook.</a></li> </ul> <h2>Matplotlib 3D Plot Line Plot</h2> <p>Here’s an example of the power of 3D line plots utilizing all the info above. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Standard imports import matplotlib.pyplot as plt import numpy as np # Import 3D Axes from mpl_toolkits.mplot3d import axes3d # Set up Figure and 3D Axes fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # Create space of numbers for cos and sin to be applied to theta = np.linspace(-12, 12, 200) x = np.sin(theta) y = np.cos(theta) # Create z space the same size as theta z = np.linspace(-2, 2, 200) ax.plot(x, y, z) plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img1.png" alt=""/></figure> <p>To avoid repetition, I won’t explain the points I have already made above about imports and setting up the <code>Figure</code> and <code>Axes</code> objects.</p> <p>I created the variable <code>theta</code> using <a href="https://blog.finxter.com/np-linspace/"><code>np.linspace</code></a> which returns an array of 200 numbers between -12 and 12 that are equally spaced out i.e. there is a linear distance between them all. I passed this to <code>np.sin()</code> and <code>np.cos()</code> and saved them in variables <code>x</code> and <code>y</code>.</p> <p>If you just plotted <code>x</code> and <code>y</code> now, you would get a circle. To get some up/down movement, you need to modify the z-axis. So, I used <code>np.linspace</code> again to create a list of 200 numbers equally spaced out between -2 and 2 which can be seen by looking at the z-axis (the vertical one).</p> <p>Note: if you choose a smaller number of values for <code>np.linspace</code> the plot is not as smooth.</p> <figure class="wp-block-image is-resized"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img2.png" alt="" width="349" height="231"/></figure> <p>For this plot, I set the third argument of <code>np.linspace</code> to 25 instead of 200. Clearly, this plot is much less smooth than the original and hopefully gives you an understanding of what is happening under the hood with these plots. 3D plots can seem daunting at first so my best advice is to go through the code line by line.</p> <h2>Matplotlib 3D Plot Scatter</h2> <p>Creating a scatter plot is exactly the same as making a line plot but you call <code>ax.scatter</code> instead.</p> <p>Here’s a cool plot that I adapted from <a href="https://www.youtube.com/watch?v=wJQIGXSq504" target="_blank" rel="noreferrer noopener">this</a> video. If you sample a normal distribution and create a 3D plot from it, you get a ball of points with the majority focused around the center and less and less the further from the center you go.</p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import random random.seed(1) # Create 3 samples from normal distribution with mean and standard deviation of 1 x = [random.normalvariate(1, 1) for _ in range(400)] y = [random.normalvariate(1, 1) for _ in range(400)] z = [random.normalvariate(1, 1) for _ in range(400)] # Set up Figure and Axes fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # Plot ax.scatter(x, y, z) plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img3.png" alt=""/></figure> <p>First, I imported the <a href="https://blog.finxter.com/python-random-module/" target="_blank" rel="noreferrer noopener">python random module</a> and set the seed so that you can reproduce my results. Next, I used three list comprehensions to create 3 x 400 samples of a normal distribution using the <code>random.normalvariate()</code> function. Then I set up the <code>Figure</code> and <code>Axes</code> as normal and made my plot by calling <code>ax.scatter()</code>. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X, Y, Z) plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img4.png" alt=""/></figure> <p>In this example, I plotted the same <code>X</code>, <code>Y</code> and <code>Z</code> lists as in the very first example. I want to highlight to you that some of the points are darker and some are more transparent – this indicates depth. The ones that are darker in color are in the foreground and those further back are more see-through.</p> <p>If you plot this in IPython or an interactive Jupyter Notebook window and you rotate the plot, you will see that the transparency of each point changes as you rotate.</p> <h2>Matplotlib 3D Plot Rotate</h2> <p>The easiest way to rotate 3D plots is to have them appear in an interactive window by using the Jupyter magic command <code>%matplotlib notebook</code> or using IPython (which always displays plots in interactive windows). This lets you manually rotate them by clicking and dragging. If you right-click and move the mouse, you will zoom in and out of the plot. To save a static version of the plot, click the save icon.</p> <p>It is possible to rotate plots and even create animations via code but that is out of the scope of this article.</p> <h2>Matplotlib 3D Plot Axis Labels</h2> <p>Setting axis labels for 3D plots is identical for 2D plots except now there is a third axis – the z-axis – you can label.</p> <p>You have 2 options:</p> <ol> <li>Use the <code>ax.set_xlabel()</code>, <code>ax.set_ylabel()</code> and <code>ax.set_zlabel()</code> methods, or</li> <li>Use the <code>ax.set()</code> method and pass it the keyword arguments <code>xlabel</code>, <code>ylabel</code> and <code>zlabel</code>.</li> </ol> <p>Here is an example using the first method. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(X, Y, Z) # Method 1 ax.set_xlabel('X axis') ax.set_ylabel('Y axis') ax.set_zlabel('Z axis') plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img5.png" alt=""/></figure> <p>Now each axis is labeled as expected.</p> <p>You may notice that the axis labels are not particularly visible using the default settings. You can solve this by manually increasing the size of the <code>Figure</code> with the <code>figsize</code> argument in your <code>plt.figure()</code> call.</p> <p>One thing I don’t like about method 1 is that it takes up 3 lines of code and they are boring to type. So, I much prefer method 2. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Set Figure to be 8 inches wide and 6 inches tall fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.scatter(X, Y, Z) # Method 2 - set all labels in one line of code! ax.set(xlabel='X axis', ylabel='Y axis', zlabel='Z axis') plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img6.png" alt=""/></figure> <p>Much better! Firstly, because you increased the size of the <code>Figure</code>, all the axis labels are clearly visible. Plus, it only took you one line of code to label them all. In general, if you ever use a <code>ax.set_<something>()</code> method in matplotlib, it can be written as <code>ax.set(<something>=)</code> instead. This saves you space and is nicer to type, especially if you want to make numerous modifications to the graph such as also adding a title.</p> <h2>Matplotlib 3D Plot Legend</h2> <p>You add legends to 3D plots in the exact same way you add <a href="https://blog.finxter.com/matplotlib-legend/" target="_blank" rel="noreferrer noopener">legends </a>to any other plots. Use the <code>label</code> keyword argument and then call <code>ax.legend()</code> at the end. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">import random random.seed(1) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # Plot and label original data ax.scatter(X, Y, Z, label='First Plot') # Randomly re-order the data for data in [X, Y, Z]: random.shuffle(data) # Plot and label re-ordered data ax.scatter(X, Y, Z, label='Second Plot') ax.legend(loc='upper left') plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img7.png" alt=""/></figure> <p>In this example, I first set the random seed to 1 so that you can reproduce the same results as me. I set up the <code>Figure</code> and <code>Axes</code> as expected, made my first 3D plot using <code>X</code>, <code>Y</code> and <code>Z</code> and labeled it with the <code>label</code> keyword argument and an appropriate string.</p> <p>To save me from manually creating a brand new dataset, I thought it would be a good idea to make use of the data I already had. So, I applied the <code>random.shuffle()</code> function to each of <code>X</code>, <code>Y</code> and <code>Z</code> which mixes the values of the lists in place. So, calling <code>ax.plot()</code> the second time, plotted the same numbers but in a different order, thus producing a different looking plot. Finally, I labeled the second plot and called <code>ax.legend(loc='upper left')</code> to display a legend in the upper left corner of the plot.</p> <p>All the usual things you can do with legends are still possible for 3D plots. If you want to learn more than these basic steps, check out my <a href="https://blog.finxter.com/matplotlib-legend/">comprehensive guide to legends in matplotlib</a>.</p> <p>Note: If you run the above code again, you will get a different looking plot. This is because you will start with the shuffled <code>X</code>, <code>Y</code> and <code>Z</code> lists rather than the originals you created further up inb the post.</p> <h2>Matplotlib 3D Plot Background Color</h2> <p>There are two backgrounds you can modify in matplotlib – the <code>Figure</code> and the <code>Axes</code> background. Both can be set using either the <code>.set_facecolor('color')</code> or the <code>.set(facecolor='color')</code> methods. Hopefully, you know by now that I much prefer the second method over the first!</p> <p>Here’s an example where I set the <code>Figure</code> background color to green and the <code>Axes</code> background color to red. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.plot(X, Y, Z) # Axes color is red ax.set(facecolor='r') # Figure color is green fig.set(facecolor='g') plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img8.png" alt=""/></figure> <p>The first three lines are the same as a simple line plot. Then I called <code>ax.set(facecolor='r')</code> to set the <code>Axes</code> color to red and <code>fig.set(facecolor='g')</code> to set the <code>Figure</code> color to green.</p> <p>In an example with one <code>Axes</code>, it looks a bit odd to set the <code>Figure</code> and <code>Axes</code> colors separately. If you have more than one <code>Axes</code> object, it looks much better. </p> <pre class="EnlighterJSRAW" data-enlighter-language="python" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group=""># Set up Figure and Axes in one function call fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 6), subplot_kw=dict(projection='3d')) colors = ['r', 'g', 'y', 'b'] # iterate over colors and all Axes objects for c, ax in zip(colors, axes.flat): ax.plot(X, Y, Z) # Set Axes color ax.set(facecolor=c) # Set Figure color fig.set(facecolor='pink') plt.show() </pre> <figure class="wp-block-image"><img src="https://raw.githubusercontent.com/theadammurphy/matplotlib_articles/master/3dplot/final_html/img/img9.png" alt=""/></figure> <p>In this example, I used <code>plt.subplots()</code> to set up an 8×6 inch <code>Figure</code> containing four 3D <code>Axes</code> objects in a 2×2 grid. The <code>subplot_kw</code> argument accepts a dictionary of values and these are passed to <code>add_subplot</code> to make each <code>Axes</code> object. For more info on using <code>plt.subplots()</code> check out <a href="https://blog.finxter.com/matplotlib-subplots/" target="_blank" rel="noreferrer noopener">my article</a>.</p> <p>Then I created the list <code>colors</code> containing 4 matplotlib color strings. After that, I used a for loop to iterate over <code>colors</code> and <code>axes.flat</code>. In order to iterate over <code>colors</code> and <code>axes</code> together, they need to be the same shape. There are several ways to do this but using the <code>.flat</code> attribute works well in this case.</p> <p>Finally, I made the same plot on each <code>Axes</code> and set the facecolors. It is clear now why setting a <code>Figure</code> color can be more useful if you create subplots – there is more space for the color to shine through.</p> <h2>Conclusion</h2> <p>That’s it, you now know the basics of creating 3D plots in matplotlib!</p> <p>You’ve learned the necessary imports you need and also how to set up your <code>Figure</code> and <code>Axes</code> objects to be 3D. You’ve looked at examples of line and scatter plots. Plus, you can modify these by rotating them, adding axis labels, adding legends and changing the background color.</p> <p>There is still more to be learned about 3D plots such as surface plots, wireframe plots, animating them and changing the aspect ratio but I’ll leave those for another article.</p> <h2>Where To Go From Here?</h2> <p>Do you wish you could be a programmer full-time but don’t know how to start?</p> <p>Check out the pure value-packed webinar where Chris – creator of Finxter.com – teaches you to become a Python freelancer in 60 days or your money back!</p> <p><a href="https://tinyurl.com/become-a-python-freelancer">https://tinyurl.com/become-a-python-freelancer</a></p> <p>It doesn’t matter if you’re a Python novice or Python pro. If you are not making six figures/year with Python right now, you will learn something from this webinar.</p> <p>These are proven, no-BS methods that get you results fast.</p> <p>This webinar won’t be online forever. Click the link below before the seats fill up and learn how to become a Python freelancer, guaranteed.</p> <p><a href="https://tinyurl.com/become-a-python-freelancer">https://tinyurl.com/become-a-python-freelancer</a></p> </div> https://www.sickgaming.net/blog/2020/04/01/matplotlib-3d-plot-a-helpful-illustrated-guide/ |