<jupyter_start><jupyter_text>Data Visualization using Python, Matplotlib and Seaborn Part 8 of "Data Analysis with Python: Zero to Pandas"This tutorial is the eighth in a series on introduction to programming and data analysis using the Python language. These tutorials take a practical coding-based approach, and the best way to learn the material is to execute the code and experiment with the examples. Check out the full series here: 1. [First Steps with Python and Jupyter](https://jovian.ml/aakashns/first-steps-with-python)2. [A Quick Tour of Variables and Data Types](https://jovian.ml/aakashns/python-variables-and-data-types)3. [Branching using Conditional Statements and Loops](https://jovian.ml/aakashns/python-branching-and-loops)4. [Writing Reusable Code Using Functions](https://jovian.ml/aakashns/python-functions-and-scope)5. [Reading from and Writing to Files](https://jovian.ml/aakashns/python-os-and-filesystem)6. [Numerical Computing with Python and Numpy](https://jovian.ml/aakashns/python-numerical-computing-with-numpy)7. [Analyzing Tabular Data using Pandas](https://jovian.ml/aakashns/python-pandas-data-analysis)8. [Data Visulation using Matplotlib & Seaborn](https://jovian.ml/aakashns/python-matplotlib-data-visualization)  IntroductionData visualization is the graphic representation of data. It involves producing images that communicate relationships among the represented data to viewers. Visualizing data is an essential part of data analysis and machine learning. In this tutorial, we'll use Python libraries [Matplotlib](https://matplotlib.org) and [Seaborn](https://seaborn.pydata.org) to learn and apply some popular data visualization techniques.To begin let's import the libraries. We'll use the `matplotlib.pyplot` for basic plots like line & bar charts. It is often imported with the alias `plt`. The `seaborn` module will be used for more advanced plots, and it is imported with the alias `sns`.<jupyter_code># Uncomment the next line to install the required libraries
!pip install matplotlib seaborn --upgrade --quiet
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Notice this we also include the special command `%matplotlib inline` to ensure that plots are shown and embedded within the Jupyter notebook itself. Without this command, sometimes plots may show up in pop-up windows.  Line ChartLine charts are one of the simplest and most widely used data visualization techniques. A line chart displays information as a series of data points or markers, connected by straight lines. You can customize the shape, size, color and other aesthetic elements of the markers and lines for better visual clarity.Here's a Python list showing the yield of apples (tons per hectare) over 6 years in an imaginary country called Kanto.<jupyter_code>yield_apples = [0.895, 0.91, 0.919, 0.926, 0.929, 0.931]<jupyter_output><empty_output><jupyter_text>We can visualize how the yield of apples changes over time using a line chart. To draw a line chart, we can use the `plt.plot` function.<jupyter_code>plt.plot(yield_apples)<jupyter_output><empty_output><jupyter_text>Calling the `plt.plot` function draws the line chart as expected, and also returns a list of plots drawn `[]` shown within the output. We can include a semicolon (`;`) at the end of the last statement in the cell to avoiding showing the output and just display the graph.<jupyter_code>plt.plot(yield_apples);<jupyter_output><empty_output><jupyter_text>Let's enhance this plot step-by-step to make it more informative and beautiful.  Customizing the X-axisThe X-axis of the plot currently shows list element indexes 0 to 5. The plot would be more informative if we could show the year for which the data is being plotted. We can do this by two arguments `plt.plot`.<jupyter_code>years = [2010, 2011, 2012, 2013, 2014, 2015]
yield_apples = [0.895, 0.91, 0.919, 0.926, 0.929, 0.931]
plt.plot(years, yield_apples)<jupyter_output><empty_output><jupyter_text>Axis LabelsWe can add labels to the axes to show what each axis represents using the `plt.xlabel` and `plt.ylabel` methods.<jupyter_code>plt.plot(years, yield_apples)
plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)');<jupyter_output><empty_output><jupyter_text>Plotting Multiple LinesIt's really easy to plot multiple lines in the same graph. Just invoke the `plt.plot` function multiple times. Let's compare the yields of apples vs. oranges in Kanto.<jupyter_code>years = range(2000, 2012)
apples = [0.895, 0.91, 0.919, 0.926, 0.929, 0.931, 0.934, 0.936, 0.937, 0.9375, 0.9372, 0.939]
oranges = [0.962, 0.941, 0.930, 0.923, 0.918, 0.908, 0.907, 0.904, 0.901, 0.898, 0.9, 0.896, ]
plt.plot(years, apples)
plt.plot(years, oranges)
plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)');<jupyter_output><empty_output><jupyter_text>Chart Title and  LegendTo differentiate between multiple lines, we can include a legend within the graph using the `plt.legend` function. We also give the entire chart a title using the `plt.title` function.<jupyter_code>plt.plot(years, apples)
plt.plot(years, oranges)

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);<jupyter_output><empty_output><jupyter_text>Line MarkersWe can also show markers for the data points on each line using the `marker` argument of `plt.plot`. Matplotlib supports many different types of markers like circle, cross, square, diamond etc. You can find the full list of marker types here: https://matplotlib.org/3.1.1/api/markers_api.html<jupyter_code>plt.plot(years, apples, marker='o')
plt.plot(years, oranges, marker='x')

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);<jupyter_output><empty_output><jupyter_text>Styling lines and markersThe `plt.plot` function supports many arguments for styling lines and markers:- `color` or `c`: set the color of the line ([supported colors](https://matplotlib.org/3.1.0/gallery/color/named_colors.html))- `linestyle` or `ls`: choose between a solid or dashed line- `linewidth` or `lw`: set the width of a line- `markersize` or `ms`: set the size of markers- `markeredgecolor` or `mec`: set the edge color for markers- `markeredgewidth` or `mew`: set the edge width for markers- `markerfacecolor` or `mfc`: set the fill color for markers- `alpha`: opacity of the plotCheck out the documentation for `plt.plot` to learn more: [https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.htmlmatplotlib.pyplot.plot](https://matplotlib.org/api/_as_gen/matplotlib.pyplot.plot.htmlmatplotlib.pyplot.plot)<jupyter_code>plt.plot(years, apples, marker='s', c='b', ls='-', lw=2, ms=8, mew=2, mec='navy')
plt.plot(years, oranges, marker='o', c='r', ls='--', lw=3, ms=10, alpha=.5)

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);<jupyter_output><empty_output><jupyter_text>The `fmt` argument provides a shorthand for specifying the line style, marker and line color. It can be provided as the third argument to `plt.plot`.```fmt = '[marker][line][color]'```<jupyter_code>plt.plot(years, apples, 's-b')
plt.plot(years, oranges, 'o--r')

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);<jupyter_output><empty_output><jupyter_text>If no line style is specified in `fmt`, only markers are drawn.<jupyter_code>plt.plot(years, oranges, 'or')
plt.title("Yield of Oranges (tons per hectare)");<jupyter_output><empty_output><jupyter_text>Changing the Figure SizeYou can use the `plt.figure` function to change the size of the figure.<jupyter_code>plt.figure(figsize=(12, 6))

plt.plot(years, oranges, 'or')
plt.title("Yield of Oranges (tons per hectare)");<jupyter_output><empty_output><jupyter_text>Improving Default Styles using SeabornAn easy way to make your charts look beautiful is to use some default styles provided in the Seaborn library. These can be applied globally using the `sns.set_style` function. You can see a full list of predefined styles here: https://seaborn.pydata.org/generated/seaborn.set_style.html<jupyter_code>sns.set_style("whitegrid")
plt.plot(years, apples, 's-b')
plt.plot(years, oranges, 'o--r')

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);
sns.set_style("darkgrid")
plt.plot(years, apples, 's-b')
plt.plot(years, oranges, 'o--r')

plt.xlabel('Year')
plt.ylabel('Yield (tons per hectare)')

plt.title("Crop Yields in Kanto")
plt.legend(['Apples', 'Oranges']);
plt.plot(years, oranges, 'or')
plt.title("Yield of Oranges (tons per hectare)");<jupyter_output><empty_output><jupyter_text>You can also edit default styles directly by modifying the `matplotlib.rcParams` dictionary. Learn more: https://matplotlib.org/3.2.1/tutorials/introductory/customizing.htmlmatplotlib-rcparams<jupyter_code>import matplotlib
matplotlib.rcParams['font.size'] = 14
matplotlib.rcParams['figure.figsize'] = (9, 5)
matplotlib.rcParams['figure.facecolor'] = '#00000000'<jupyter_output><empty_output><jupyter_text>Save and UploadWhether you're running this Jupyter notebook on an online service like Binder or on your local machine, it's important to save your work from time, so that you can access it later, or share it online. You can upload this notebook to your [Jovian.ml](https://jovian.ml) account using the `jovian` Python library.<jupyter_code># Install the library 
!pip install jovian --upgrade --quiet
import jovian
jovian.commit(project='python-matplotlib-data-visualization')<jupyter_output><empty_output><jupyter_text>Scatter PlotIn a scatter plot, the values of 2 variables are plotted as points on a 2-dimensional grid. Additionally, you can also use a third variable to determine the size or color of the points. Let's try out an example.The [Iris flower dataset](https://en.wikipedia.org/wiki/Iris_flower_data_set) provides samples measurements of sepals and petals for 3 species of flowers. The Iris dataset is included with the Seaborn library, and can be loaded as a Pandas data frame.<jupyter_code># Load data into a Pandas dataframe
flowers_df = sns.load_dataset("iris")
flowers_df
flowers_df.species.unique()<jupyter_output><empty_output><jupyter_text>Let's try to visualize the relationship between sepal length and sepal width. Our first instinct might be to create a line chart using `plt.plot`. However, the output is not very informative as there are too many combinations of the two properties within the dataset, and there doesn't seem to be simple relationship between them.<jupyter_code>plt.plot(flowers_df.sepal_length, flowers_df.sepal_width);<jupyter_output><empty_output><jupyter_text>We can use a scatter plot to visualize how sepal length & sepal width vary using the `scatterplot` function from `seaborn` (imported as `sns`).<jupyter_code>sns.scatterplot(flowers_df.sepal_length, flowers_df.sepal_width);<jupyter_output><empty_output><jupyter_text>Adding HuesNotice how the points in the above plot seem to form distinct clusters with some outliers. We can color the dots using the flower species as a `hue`. We can also make the points larger using the `s` argument.<jupyter_code>sns.scatterplot(flowers_df.sepal_length, flowers_df.sepal_width, hue=flowers_df.species, s=100);<jupyter_output><empty_output><jupyter_text>Adding hues makes the plot more informative. We can immediately tell that flowers of the Setosa species have a smaller sepal length but higher sepal widths, while the opposite holds true for the Virginica species.   Customizing Seaborn FiguresSince Seaborn uses Matplotlib's plotting functions internally, we can use functions like `plt.figure` and `plt.title` to modify the figure.<jupyter_code>plt.figure(figsize=(12, 6))
plt.title('Sepal Dimensions')

sns.scatterplot(flowers_df.sepal_length, 
                flowers_df.sepal_width, 
                hue=flowers_df.species,
                s=100);<jupyter_output><empty_output><jupyter_text>Plotting using Pandas Data FramesSeaborn has in-built support for Pandas data frames. Instead of passing each column as a series, you can also pass column names and use the `data` argument to pass the data frame.<jupyter_code>plt.title('Sepal Dimensions')
sns.scatterplot('sepal_length', 
                'sepal_width', 
                hue='species',
                s=100,
                data=flowers_df);<jupyter_output><empty_output><jupyter_text>Let's save and upload our work before continuing.<jupyter_code>import jovian
jovian.commit()<jupyter_output><empty_output><jupyter_text>HistogramA histogram represents the distribution of data by forming bins along the range of the data and then drawing bars to show the number of observations that fall in each bin.As an example, let's visualize the how the values of sepal width in the flowers dataset are distributed. We can use the `plt.hist` function to create a histogram.<jupyter_code># Load data into a Pandas dataframe
flowers_df = sns.load_dataset("iris")
flowers_df.sepal_width
plt.title("Distribution of Sepal Width")
plt.hist(flowers_df.sepal_width);<jupyter_output><empty_output><jupyter_text>We can immediately see that values of sepal width fall in the range 2.0 - 4.5, and around 35 values are in the range 2.9 - 3.1, which seems to be the largest bin.  Controlling the size and number of binsWe can control the number of bins, or the size of each bin using the `bins` argument.<jupyter_code># Specifying the number of bins
plt.hist(flowers_df.sepal_width, bins=5);
import numpy as np

# Specifying the boundaries of each bin
plt.hist(flowers_df.sepal_width, bins=np.arange(2, 5, 0.25));
# Bins of unequal sizes
plt.hist(flowers_df.sepal_width, bins=[1, 3, 4, 4.5]);<jupyter_output><empty_output><jupyter_text>Multiple HistogramsSimilar to line charts, we can draw multiple histograms in a single chart. We can reduce the opacity of each histogram, so the the bars of one histogram don't hide the bars for others.Let's draw separate histograms for each species of flowers.<jupyter_code>setosa_df = flowers_df[flowers_df.species == 'setosa']
versicolor_df = flowers_df[flowers_df.species == 'versicolor']
virginica_df = flowers_df[flowers_df.species == 'virginica']
plt.hist(setosa_df.sepal_width, alpha=0.4, bins=np.arange(2, 5, 0.25));
plt.hist(versicolor_df.sepal_width, alpha=0.4, bins=np.arange(2, 5, 0.25));<jupyter_output><empty_output><jupyter_text>We can also stack multiple histograms on top of one another.<jupyter_code>plt.title('Distribution of Sepal Width')

plt.hist([setosa_df.sepal_width, versicolor_df.sepal_width, virginica_df.sepal_width], 
         bins=np.arange(2, 5, 0.25), 
         stacked=True);

plt.legend(['Setosa', 'Versicolor', 'Virginica']);<jupyter_output><empty_output><jupyter_text>Let's save and commit our work before continuing<jupyter_code>import jovian
jovian.commit()<jupyter_output><empty_output><jupyter_text>Bar ChartBar charts are quite similar to line charts i.e. they show a sequence of values, however a bar is shown for each value, rather than points connected by lines. We can use the `plt.bar` function to draw a bar chart.<jupyter_code>years = range(2000, 2006)
apples = [0.35, 0.6, 0.9, 0.8, 0.65, 0.8]
oranges = [0.4, 0.8, 0.9, 0.7, 0.6, 0.8]
plt.bar(years, oranges);<jupyter_output><empty_output><jupyter_text>Like histograms, bars can also be stacked on top of one another. We use the `bottom` argument to `plt.bar` to achieve this.<jupyter_code>plt.bar(years, apples)
plt.bar(years, oranges, bottom=apples);<jupyter_output><empty_output><jupyter_text>Bar Plots with AveragesLet's look at another sample dataset included with Seaborn, called "tips". The dataset contains information about the sex, time of day, total bill and tip amount for customers visiting a restaurant over a week.<jupyter_code>tips_df = sns.load_dataset("tips");
tips_df<jupyter_output><empty_output><jupyter_text>We might want to draw a bar chart to visualize how the average bill amount varies across different days of the week. One way to do this would be to compute the day-wise averages and then use `plt.bar` (try it as an exercise).However, since this is a very common use case, the Seaborn library provides a `barplot` function which can automatically compute averages.<jupyter_code>sns.barplot('day', 'total_bill', data=tips_df);<jupyter_output><empty_output><jupyter_text>The lines cutting each bar represent the amount of variation in the values. For instance, it seems like the variation in the total bill was quite high on Fridays, and lower on Saturday.We can also specify a `hue` argument to compare bar plots side-by-side based on a third feature e.g. sex.<jupyter_code>sns.barplot('day', 'total_bill', hue='sex', data=tips_df);<jupyter_output><empty_output><jupyter_text>You can make the bars horizontal simply by switching the axes.<jupyter_code>sns.barplot('total_bill', 'day', hue='sex', data=tips_df);<jupyter_output><empty_output><jupyter_text>Let's save and commit our work before continuing<jupyter_code>import jovian
jovian.commit()<jupyter_output><empty_output><jupyter_text>HeatmapA heatmap is used to visualize 2-dimensional data like a matrix or a table using colors. The best way to understand it is by looking at an example. We'll use another sample dataset from Seaborn, called "flights", to visualize monthly passenger footfall at an airport over 12 years.<jupyter_code>flights_df = sns.load_dataset("flights").pivot("month", "year", "passengers")
flights_df<jupyter_output><empty_output><jupyter_text>`flights_df` is a matrix with one row for each month and one column of each year. The values in the matrix show the number of passengers (in thousands) that visited the airport in a specific month of a specific year. We can use the `sns.heatmap` function to visualize the footfall at the airport.<jupyter_code>plt.title("No. of Passengers (1000s)")
sns.heatmap(flights_df);<jupyter_output><empty_output><jupyter_text>The brighter colors indicate a higher footfall at the airport. By looking at the graph, we can infer two things:- The footfall at the airport in any given year tends to be the highest around July & August.- The footfall at the airport in any given month tends to grow year by year.We can also display the actual values in each block by specifying `annot=True`, and use the `cmap` argument to change the color palette.<jupyter_code>plt.title("No. of Passengers (1000s)")
sns.heatmap(flights_df, fmt="d", annot=True, cmap='Blues');<jupyter_output><empty_output><jupyter_text>Let's save and upload our work before continuing.<jupyter_code>import jovian
jovian.commit()<jupyter_output><empty_output><jupyter_text>ImagesMatplotlib can also be used to display images. Let's download an image from the internet.<jupyter_code>from urllib.request import urlretrieve
urlretrieve('https://i.imgur.com/SkPbq.jpg', 'chart.jpg');<jupyter_output><empty_output><jupyter_text>Before an image can be displayed, it has to be read into memory using the `PIL` module<jupyter_code>from PIL import Image
img = Image.open('chart.jpg')<jupyter_output><empty_output><jupyter_text>An image loaded using PIL is simply a 3-dimensional numpy array containing pixel intensities for the red, green & blue (RGB) channels of the image. We can convert the image into an array using `np.array`.<jupyter_code>img_array = np.array(img)
img_array.shape<jupyter_output><empty_output><jupyter_text>We can display the PIL image using `plt.imshow`.<jupyter_code>plt.imshow(img);<jupyter_output><empty_output><jupyter_text>We can turn off the axes & grid lines, and show a title using the relevant functions.<jupyter_code>plt.grid(False)
plt.title('A data science meme')
plt.axis('off')
plt.imshow(img);<jupyter_output><empty_output><jupyter_text>To display a part of the image, we can simply select a slice from the numpy array.<jupyter_code>plt.grid(False)
plt.axis('off')
plt.imshow(img_array[125:325,105:305]);<jupyter_output><empty_output><jupyter_text>Plotting multiple charts in a gridMatplotlib and Seaborn also support plotting multiple charts in a grid, using `plt.subplots`, which returns a set of axes that can be used for plotting. Here's a single grid showing the different types of charts we've covered in this tutorial.<jupyter_code>fig, axes = plt.subplots(2, 3, figsize=(16, 8))

# Use the axes for plotting
axes[0,0].plot(years, apples, 's-b')
axes[0,0].plot(years, oranges, 'o--r')
axes[0,0].set_xlabel('Year')
axes[0,0].set_ylabel('Yield (tons per hectare)')
axes[0,0].legend(['Apples', 'Oranges']);
axes[0,0].set_title('Crop Yields in Kanto')


# Pass the axes into seaborn
axes[0,1].set_title('Sepal Length vs. Sepal Width')
sns.scatterplot(flowers_df.sepal_length, 
                flowers_df.sepal_width, 
                hue=flowers_df.species, 
                s=100, 
                ax=axes[0,1]);

# Use the axes for plotting
axes[0,2].set_title('Distribution of Sepal Width')
axes[0,2].hist([setosa_df.sepal_width, versicolor_df.sepal_width, virginica_df.sepal_width], 
         bins=np.arange(2, 5, 0.25), 
         stacked=True);

axes[0,2].legend(['Setosa', 'Versicolor', 'Virginica']);

# Pass the axes into seaborn
axes[1,0].set_title('Restaurant bills')
sns.barplot('day', 'total_bill', hue='sex', data=tips_df, ax=axes[1,0]);

# Pass the axes into seaborn
axes[1,1].set_title('Flight traffic')
sns.heatmap(flights_df, cmap='Blues', ax=axes[1,1]);

# Plot an image using the axes
axes[1,2].set_title('Data Science Meme')
axes[1,2].imshow(img)
axes[1,2].grid(False)
axes[1,2].set_xticks([])
axes[1,2].set_yticks([])

plt.tight_layout(pad=2);<jupyter_output><empty_output><jupyter_text>See this page for a full list of supported functions: https://matplotlib.org/3.3.1/api/axes_api.htmlthe-axes-class.  Pair plots with SeabornSeaborn also provides a helper function `sns.pairplot` to automatically plot different types of charts for pairs of features within a dataframe.<jupyter_code>sns.pairplot(flowers_df, hue='species');
sns.pairplot(tips_df, hue='sex');<jupyter_output><empty_output><jupyter_text>Let's save and upload our work before continuing.<jupyter_code>import jovian
jovian.commit()<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10483581553920689, 'fraction_numerical': 0.01731562892517495, 'mean_word_length': 5.807022900763359, 'pattern_counts': {'":': 0, '<': 193, '<?xml version=': 0, '>': 193, 'https://': 20, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '48', 'dir': 'jupyter-structured-clean-dedup', 'id': '430309', 'license': 'MIT', 'n_tokens_mistral': 7719, 'n_tokens_neox': 7076, 'n_words': 2376, 'path': '5-python-matplotlib-seaborn-data-visualization/python-matplotlib-data-visualization.ipynb', 'repo_name': 'tusharnankani/data-analysis-with-python', 'size': '22292'}