From 081b7d126edd69e968811cc5017ea2244869c73d Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Sat, 24 Dec 2022 20:39:10 -0800 Subject: [PATCH 01/16] code formatting for viz --- source/viz.md | 993 ++++++++++++++++++++++++++++---------------------- 1 file changed, 553 insertions(+), 440 deletions(-) diff --git a/source/viz.md b/source/viz.md index b9c6c0bc..34933069 100644 --- a/source/viz.md +++ b/source/viz.md @@ -15,23 +15,27 @@ kernelspec: (viz)= # Effective data visualization -## Overview +## Overview This chapter will introduce concepts and tools relating to data visualization beyond what we have seen and practiced so far. We will focus on guiding principles for effective data visualization and explaining visualizations independent of any particular tool or programming language. In the process, we will cover some specifics of creating visualizations (scatter plots, bar -plots, line plots, and histograms) for data using Python. +plots, line plots, and histograms) for data using Python. ## Chapter learning objectives By the end of the chapter, readers will be able to do the following: - +- Describe when to use the following kinds of visualizations to answer specific questions using a data set: + - scatter plots + - line plots + - bar plots + - histogram plots - Given a data set and a question, select from the above plot types and use Python to create a visualization that best answers the question. - Given a visualization and a question, evaluate the effectiveness of the visualization and suggest improvements to better answer the question. - Referring to the visualization, communicate the conclusions in non-technical terms. -- Identify rules of thumb for creating effective visualizations. +- Identify rules of thumb for creating effective visualizations. - Define the two key aspects of altair objects: - mark objects - encodings @@ -58,22 +62,22 @@ Imagine your visualization as part of a poster presentation for a project; even if you aren't standing at the poster explaining things, an effective visualization will convey your message to the audience. -Recall the different data analysis questions -from Chapter \@ref(intro). -With the visualizations we will cover in this chapter, -we will be able to answer *only descriptive and exploratory* questions. -Be careful to not answer any *predictive, inferential, causal* -*or mechanistic* questions with the visualizations presented here, -as we have not learned the tools necessary to do that properly just yet. +Recall the different data analysis questions +from Chapter \@ref(intro). +With the visualizations we will cover in this chapter, +we will be able to answer *only descriptive and exploratory* questions. +Be careful to not answer any *predictive, inferential, causal* +*or mechanistic* questions with the visualizations presented here, +as we have not learned the tools necessary to do that properly just yet. As with most coding tasks, it is totally fine (and quite common) to make mistakes and iterate a few times before you find the right visualization for your data and question. There are many different kinds of plotting -graphics available to use (see Chapter 5 of *Fundamentals of Data Visualization* {cite:p}`wilkeviz` for a directory). +graphics available to use (see Chapter 5 of *Fundamentals of Data Visualization* {cite:p}`wilkeviz` for a directory). The types of plot that we introduce in this book are shown in {numref}`plot_sketches` -which one you should select depends on your data -and the question you want to answer. -In general, the guiding principles of when to use each type of plot +which one you should select depends on your data +and the question you want to answer. +In general, the guiding principles of when to use each type of plot are as follows: ```{index} visualization; line, visualization; histogram, visualization; scatter, visualization; bar, distribution @@ -112,7 +116,7 @@ Just being able to make a visualization in Python with `altair` (or any other to for that matter) doesn't mean that it effectively communicates your message to others. Once you have selected a broad type of visualization to use, you will have to refine it to suit your particular need. Some rules of thumb for doing -this are listed below. They generally fall into two classes: you want to +this are listed below. They generally fall into two classes: you want to *make your visualization convey your message*, and you want to *reduce visual noise* as much as possible. Humans have limited cognitive ability to process information; both of these types of refinement aim to reduce the mental load on @@ -126,9 +130,9 @@ understand and remember your message quickly. - Ensure the text, symbols, lines, etc., on your visualization are big enough to be easily read. - Ensure the data are clearly visible; don't hide the shape/distribution of the data behind other objects (e.g., a bar). - Make sure to use color schemes that are understandable by those with - colorblindness (a surprisingly large fraction of the overall + colorblindness (a surprisingly large fraction of the overall population—from about 1% to 10%, depending on sex and ancestry {cite:p}`deebblind`). - For example, [Color Schemes](https://vega.github.io/vega/docs/schemes/) + For example, [Color Schemes](https://vega.github.io/vega/docs/schemes/) provides the ability to pick such color schemes, and you can check your visualizations after you have created them by uploading to online tools such as a [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/). @@ -136,7 +140,7 @@ understand and remember your message quickly. **Minimize noise** -- Use colors sparingly. Too many different colors can be distracting, create false patterns, and detract from the message. +- Use colors sparingly. Too many different colors can be distracting, create false patterns, and detract from the message. - Be wary of overplotting. Overplotting is when marks that represent the data overlap, and is problematic as it prevents you from seeing how many data points are represented in areas of the visualization where this occurs. If your @@ -147,14 +151,14 @@ understand and remember your message quickly. +++ -## Creating visualizations with `altair` +## Creating visualizations with `altair` #### *Build the visualization iteratively* ```{index} altair ``` -This section will cover examples of how to choose and refine a visualization given a data set and a question that you want to answer, -and then how to create the visualization in Python using `altair`. To use the `altair` package, we need to import the `altair` package. We will also import `pandas` in order to support reading and other data related operations. +This section will cover examples of how to choose and refine a visualization given a data set and a question that you want to answer, +and then how to create the visualization in Python using `altair`. To use the `altair` package, we need to import the `altair` package. We will also import `pandas` to use for reading in the data. ```{code-cell} ipython3 import pandas as pd @@ -172,12 +176,12 @@ from myst_nb import glue ```{index} Mauna Loa ``` -The [Mauna Loa CO$_{\text{2}}$ data set](https://www.esrl.noaa.gov/gmd/ccgg/trends/data.html), -curated by Dr. Pieter Tans, NOAA/GML +The [Mauna Loa CO$_{\text{2}}$ data set](https://www.esrl.noaa.gov/gmd/ccgg/trends/data.html), +curated by Dr. Pieter Tans, NOAA/GML and Dr. Ralph Keeling, Scripps Institution of Oceanography, -records the atmospheric concentration of carbon dioxide -(CO$_{\text{2}}$, in parts per million) -at the Mauna Loa research station in Hawaii +records the atmospheric concentration of carbon dioxide +(CO$_{\text{2}}$, in parts per million) +at the Mauna Loa research station in Hawaii from 1959 onward {cite:p}`maunadata`. For this book, we are going to focus on the last 40 years of the data set, 1980-2020. @@ -185,61 +189,52 @@ For this book, we are going to focus on the last 40 years of the data set, ```{index} question; visualization ``` -**Question:** Does the concentration of atmospheric CO$_{\text{2}}$ change over time, +**Question:** Does the concentration of atmospheric CO$_{\text{2}}$ change over time, and are there any interesting patterns to note? -```{code-cell} ipython3 -:tags: ["remove-cell"] -mauna_loa = pd.read_csv("data/mauna_loa.csv") -mauna_loa['day']=1 -mauna_loa['date_measured']=pd.to_datetime(mauna_loa[["year", "month", "day"]]) -mauna_loa = mauna_loa[['date_measured', 'ppm']].query('ppm>0 and date_measured>"1980-1-1"') -mauna_loa.to_csv("data/mauna_loa_data.csv", index=False) -``` - - - To get started, we will read and inspect the data: ```{code-cell} ipython3 # mauna loa carbon dioxide data -co2_df = pd.read_csv("data/mauna_loa_data.csv", parse_dates=['date_measured']) +co2_df = pd.read_csv( + "data/mauna_loa_data.csv", parse_dates=['date_measured'] +) co2_df ``` ```{code-cell} ipython3 -co2_df.dtypes +co2_df.info() ``` -We see that there are two columns in the `co2_df` data frame; `date_measured` and `ppm`. -The `date_measured` column holds the date the measurement was taken, +We see that there are two columns in the `co2_df` data frame; `date_measured` and `ppm`. +The `date_measured` column holds the date the measurement was taken, and is of type `datetime64`. -The `ppm` column holds the value of CO$_{\text{2}}$ in parts per million +The `ppm` column holds the value of CO$_{\text{2}}$ in parts per million that was measured on each date, and is type `float64`. > **Note:** `read_csv` was able to parse the `date_measured` column into the -> `datetime` vector type because it was entered -> in the international standard date format, -> called ISO 8601, which lists dates as `year-month-day` and we used `parse_dates=True`. -> `datetime` vectors are `double` vectors with special properties that allow +> `datetime` vector type because it was entered +> in the international standard date format, +> called ISO 8601, which lists dates as `year-month-day` and we used `parse_dates=True`. +> `datetime` vectors are `double` vectors with special properties that allow > them to handle dates correctly. -> For example, `datetime` type vectors allow functions like `altair` -> to treat them as numeric dates and not as character vectors, -> even though they contain non-numeric characters +> For example, `datetime` type vectors allow functions like `altair` +> to treat them as numeric dates and not as character vectors, +> even though they contain non-numeric characters > (e.g., in the `date_measured` column in the `co2_df` data frame). -> This means Python will not accidentally plot the dates in the wrong order -> (i.e., not alphanumerically as would happen if it was a character vector). +> This means Python will not accidentally plot the dates in the wrong order +> (i.e., not alphanumerically as would happen if it was a character vector). > More about dates and times can be viewed [here](https://wesmckinney.com/book/time-series.html) -Since we are investigating a relationship between two variables -(CO$_{\text{2}}$ concentration and date), -a scatter plot is a good place to start. -Scatter plots show the data as individual points with `x` (horizontal axis) +Since we are investigating a relationship between two variables +(CO$_{\text{2}}$ concentration and date), +a scatter plot is a good place to start. +Scatter plots show the data as individual points with `x` (horizontal axis) and `y` (vertical axis) coordinates. -Here, we will use the measurement date as the `x` coordinate -and the CO$_{\text{2}}$ concentration as the `y` coordinate. -while using the `altair` package, We create a plot object with the `alt.Chart()` function. +Here, we will use the measurement date as the `x` coordinate +and the CO$_{\text{2}}$ concentration as the `y` coordinate. +We create a plot object with the `alt.Chart()` function. There are a few basic aspects of a plot that we need to specify: ```{index} altair; geometric object, altair; geometric encoding, geometric object, geometric encoding @@ -253,7 +248,7 @@ There are a few basic aspects of a plot that we need to specify: - The **geometric encoding**, which tells `altair` how the columns in the data frame map to properties of the visualization. - To create an encoding, we use the `encode()` function. - The `encode()` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name(column names) - - Here, we set the plot `x` axis to the `date_measured` variable, and the plot `y` axis to the `ppm` variable. + - Here, we set the `x` axis of the plot to the `date_measured` variable, and on the `y` axis, we plot the `ppm` variable. ```{code-cell} ipython3 :tags: ["remove-cell"] @@ -262,10 +257,9 @@ from myst_nb import glue ```{code-cell} ipython3 co2_scatter = alt.Chart(co2_df).mark_point().encode( - x = "date_measured", - y = alt.Y("ppm", scale=alt.Scale(zero=False))) - - + x="date_measured", + y=alt.Y("ppm", scale=alt.Scale(zero=False)) +) ``` ```{code-cell} ipython3 @@ -273,30 +267,27 @@ co2_scatter = alt.Chart(co2_df).mark_point().encode( glue('co2_scatter', co2_scatter, display=False) ``` -:::{glue:figure} co2_scatter -:figwidth: 700px +:::{glue:figure} co2_scatter +:figwidth: 700px :name: co2_scatter Scatter plot of atmospheric concentration of CO$_{2}$ over time. ::: - -> **Note:** We can change the size of the point and color of the plot by specifying `mark_point(size=10, color='black')`. - -Certainly, the visualization in {numref}`co2_scatter` -shows a clear upward trend +Certainly, the visualization in {numref}`co2_scatter` +shows a clear upward trend in the atmospheric concentration of CO$_{\text{2}}$ over time. -This plot answers the first part of our question in the affirmative, -but that appears to be the only conclusion one can make -from the scatter visualization. +This plot answers the first part of our question in the affirmative, +but that appears to be the only conclusion one can make +from the scatter visualization. One important thing to note about this data is that one of the variables we are exploring is time. -Time is a special kind of quantitative variable -because it forces additional structure on the data—the -data points have a natural order. -Specifically, each observation in the data set has a predecessor -and a successor, and the order of the observations matters; changing their order +Time is a special kind of quantitative variable +because it forces additional structure on the data—the +data points have a natural order. +Specifically, each observation in the data set has a predecessor +and a successor, and the order of the observations matters; changing their order alters their meaning. In situations like this, we typically use a line plot to visualize the data. Line plots connect the sequence of `x` and `y` coordinates @@ -305,27 +296,35 @@ of the observations with line segments, thereby emphasizing their order. ```{index} altair; mark_line ``` -We can create a line plot in `altair` using the `mark_line` function. -Let's now try to visualize the `co2_df` as a line plot -with just the default arguments: +We can create a line plot in `altair` using the `mark_line` function. +Let's now try to visualize the `co2_df` as a line plot +with just the default arguments: ```{code-cell} ipython3 -co2_line = alt.Chart(co2_df).mark_line(color='black').encode( - x = "date_measured", - y = alt.Y("ppm", scale=alt.Scale(zero=False))) +co2_line = alt.Chart(co2_df).mark_line().encode( + x="date_measured", + y=alt.Y("ppm", scale=alt.Scale(zero=False)) +) +``` +For the y-axis, we also provided the argument `scale=alt.Scale(zero=False)`. +By default, `altair` chooses the y-limits based on the data and will keep `y=0` +in view. That would make it difficult to see any trends in our data since the +smallest value is >300 ppm. So by providing `scale=alt.Scale(zero=False)`, we +tell altair to choose a reasonable lower bound based on our data, and that +lower bound doesn't have to be zero. -``` ```{code-cell} ipython3 :tags: ["remove-cell"] glue('co2_line', co2_line, display=False) ``` - +> **Note:** We can change the size of the point and color of the plot by specifying +> `mark_point(size=10, color='black')` :::{glue:figure} co2_line -:figwidth: 700px +:figwidth: 700px :name: co2_line Line plot of atmospheric concentration of CO$_{2}$ over time. @@ -342,7 +341,7 @@ be a better choice for answering the question than the scatter plot was. The comparison between these two visualizations also illustrates a common issue with scatter plots: often, the points are shown too close together or even on top of one another, muddling information that would otherwise be clear -(*overplotting*). +(*overplotting*). ```{index} altair; alt.X, altair; alt.Y, altair; configure_axis ``` @@ -352,14 +351,17 @@ to refine things. This plot is fairly straightforward, and there is not much visual noise to remove. But there are a few things we must do to improve clarity, such as adding informative axis labels and making the font a more readable size. To add axis labels, we use the `title` argument along with `alt.X` and `alt.Y` functions. To -change the font size, we use the `configure_axis` function with the `titleFontSize` argument: +change the font size, we use the `configure_axis` function with the +`titleFontSize` argument. We also set the color of the line to be black as it +has a stronger contrast with the background than the default blue color. ```{code-cell} ipython3 co2_line_labels = alt.Chart(co2_df).mark_line(color='black').encode( - x = alt.X("date_measured", title = "Year"), - y = alt.Y("ppm", scale=alt.Scale(zero=False), title = "Atmospheric CO2 (ppm)")).configure_axis( - titleFontSize=12) - + x=alt.X("date_measured", title ="Year"), + y=alt.Y("ppm", scale=alt.Scale(zero=False), title="Atmospheric CO2 (ppm)") +).configure_axis( + titleFontSize=12 +) ``` ```{code-cell} ipython3 @@ -368,7 +370,7 @@ glue('co2_line_labels', co2_line_labels, display=False) ``` :::{glue:figure} co2_line_labels -:figwidth: 700px +:figwidth: 700px :name: co2_line_labels Line plot of atmospheric concentration of CO$_{2}$ over time with clearer axes and labels. @@ -382,14 +384,14 @@ Line plot of atmospheric concentration of CO$_{2}$ over time with clearer axes a Finally, let's see if we can better understand the oscillation by changing the visualization slightly. Note that it is totally fine to use a small number of visualizations to answer different aspects of the question you are trying to -answer. We will accomplish this by using *scale*, +answer. We will accomplish this by using *scale*, another important feature of `altair` that easily transforms the different variables and set limits. We scale the horizontal axis using the `alt.Scale(domain=['1990', '1993'])` by restricting the x-axis values between 1990 and 1994, and the vertical axis with the `alt.Scale(zero=False)` function, to not start the y-axis with zero. -In particular, here, we will use the `alt.Scale()` function to zoom in +In particular, here, we will use the `alt.Scale()` function to zoom in on just five years of data (say, 1990-1994). -`domain` argument takes a list of length two -to specify the upper and lower bounds to limit the axis. +`domain` argument takes a list of length two +to specify the upper and lower bounds to limit the axis. ```{code-cell} ipython3 @@ -400,10 +402,6 @@ co2_line_scale = alt.Chart(co2_df).mark_line(color='black', clip=True).encode( ).configure_axis( titleFontSize=12 ) - - - - ``` ```{code-cell} ipython3 @@ -412,31 +410,31 @@ glue('co2_line_scale', co2_line_scale, display=False) ``` :::{glue:figure} co2_line_scale -:figwidth: 700px +:figwidth: 700px :name: co2_line_scale Line plot of atmospheric concentration of CO$_{2}$ from 1990 to 1994. ::: -Interesting! It seems that each year, the atmospheric CO$_{\text{2}}$ increases until it reaches its peak somewhere around April, decreases until around late September, +Interesting! It seems that each year, the atmospheric CO$_{\text{2}}$ increases until it reaches its peak somewhere around April, decreases until around late September, and finally increases again until the end of the year. In Hawaii, there are two seasons: summer from May through October, and winter from November through April. Therefore, the oscillating pattern in CO$_{\text{2}}$ matches up fairly closely with the two seasons. As you might have noticed from the code used to create the final visualization -of the `co2_df` data frame, we used `axis=alt.Axis(tickCount=4)` to add the lines in the background to better visualise and map the values on the axis to the plot. +of the `co2_df` data frame, we used `axis=alt.Axis(tickCount=4)` to add the lines corresponding to each year in the background. This helps us to better visualise the change with each year. A useful analogy to constructing a data visualization is painting a picture. -We start with a blank canvas, -and the first thing we do is prepare the surface -for our painting by adding primer. -In our data visualization this is akin to calling `alt.Chart` +We start with a blank canvas, +and the first thing we do is prepare the surface +for our painting by adding primer. +In our data visualization this is akin to calling `alt.Chart` and specifying the data set we will be using. -Next, we sketch out the background of the painting. -In our data visualization, +Next, we sketch out the background of the painting. +In our data visualization, this would be when we map data to the axes in the `encode` function. Then we add our key visual subjects to the painting. -In our data visualization, +In our data visualization, this would be the geometric objects (e.g., `mark_point`, `mark_line`, etc.). And finally, we work on adding details and refinements to the painting. In our data visualization this would be when we fine tune axis labels, @@ -447,17 +445,17 @@ change the font, adjust the point size, and do other related things. ```{index} Old Faithful ``` -The `faithful` data set contains measurements -of the waiting time between eruptions +The `faithful` data set contains measurements +of the waiting time between eruptions and the subsequent eruption duration (in minutes) of the Old Faithful -geyser in Yellowstone National Park, Wyoming, United States. +geyser in Yellowstone National Park, Wyoming, United States. First, we will read the data and then answer the following question: ```{index} question; visualization ``` -**Question:** Is there a relationship between the waiting time before an eruption -and the duration of the eruption? +**Question:** Is there a relationship between the waiting time before an eruption +and the duration of the eruption? ```{code-cell} ipython3 faithful = pd.read_csv("data/faithful.csv") @@ -465,14 +463,14 @@ faithful ``` -Here again, we investigate the relationship between two quantitative variables -(waiting time and eruption time). -But if you look at the output of the data frame, +Here again, we investigate the relationship between two quantitative variables +(waiting time and eruption time). +But if you look at the output of the data frame, you'll notice that unlike time in the Mauna Loa CO$_{\text{2}}$ data set, neither of the variables here have a natural order to them. So a scatter plot is likely to be the most appropriate visualization. Let's create a scatter plot using the `altair` -package with the `waiting` variable on the horizontal axis, the `eruptions` +package with the `waiting` variable on the horizontal axis, the `eruptions` variable on the vertical axis, and the `mark_point` geometric object. The result is shown in {numref}`faithful_scatter`. @@ -480,10 +478,9 @@ The result is shown in {numref}`faithful_scatter`. ```{code-cell} ipython3 faithful_scatter = alt.Chart(faithful).mark_point(color='black', filled=True).encode( - x = "waiting", - y = "eruptions" + x="waiting", + y="eruptions" ) - ``` ```{code-cell} ipython3 @@ -491,8 +488,8 @@ faithful_scatter = alt.Chart(faithful).mark_point(color='black', filled=True).en glue('faithful_scatter', faithful_scatter, display=False) ``` -:::{glue:figure} faithful_scatter -:figwidth: 700px +:::{glue:figure} faithful_scatter +:figwidth: 700px :name: faithful_scatter Scatter plot of waiting time and eruption time. @@ -502,7 +499,7 @@ We can see in {numref}`faithful_scatter` that the data tend to fall into two groups: one with short waiting and eruption times, and one with long waiting and eruption times. Note that in this case, there is no overplotting: the points are generally nicely visually separated, and the pattern they form -is clear. Also, note that to make the points solid, we used `filled=True` as argument of the `mark_point` function. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. +is clear. Also, note that to make the points solid, we used `filled=True` as argument of the `mark_point` function. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. In order to refine the visualization, we need only to add axis labels and make the font more readable: @@ -510,12 +507,9 @@ labels and make the font more readable: ```{code-cell} ipython3 faithful_scatter_labels = alt.Chart(faithful).mark_circle(color='black').encode( - x = alt.X("waiting", title = "Waiting Time (mins)"), - y = alt.Y("eruptions", title = "Eruption Duration (mins)") + x=alt.X("waiting", title="Waiting Time (mins)"), + y=alt.Y("eruptions", title="Eruption Duration (mins)") ) - - - ``` @@ -525,7 +519,7 @@ glue('faithful_scatter_labels', faithful_scatter_labels, display=False) ``` :::{glue:figure} faithful_scatter_labels -:figwidth: 700px +:figwidth: 700px :name: faithful_scatter_labels Scatter plot of waiting time and eruption time with clearer axes and labels. @@ -546,7 +540,7 @@ Canadian census. ``` **Question:** Is there a relationship between -the percentage of people who speak a language as their mother tongue and +the percentage of people who speak a language as their mother tongue and the percentage for whom that is the primary language spoken at home? And is there a pattern in the strength of this relationship in the higher-level language categories (Official languages, Aboriginal languages, or @@ -570,11 +564,10 @@ We will begin with a scatter plot of the `mother_tongue` and `most_at_home` colu The resulting plot is shown in {numref}`can_lang_plot` ```{code-cell} ipython3 - can_lang_plot = alt.Chart(can_lang).mark_circle(color='black').encode( - x = "most_at_home", - y = "mother_tongue") - + x="most_at_home", + y="mother_tongue" +) ``` @@ -584,7 +577,7 @@ glue('can_lang_plot', can_lang_plot, display=False) ``` :::{glue:figure} can_lang_plot -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot Scatter plot of number of Canadians reporting a language as their mother tongue vs the primary language at home @@ -593,21 +586,27 @@ Scatter plot of number of Canadians reporting a language as their mother tongue ```{index} escape character ``` -To make an initial improvement in the interpretability -of {numref}`can_lang_plot`, we should +To make an initial improvement in the interpretability +of {numref}`can_lang_plot`, we should replace the default axis names with more informative labels. We can add a line break in the axis names so that some of the words are printed on a new line. This will make the axes labels on the plots more readable. To do this, we pass the title as a list. Each element of the list will be on a new line. -We should also increase the font size to further +We should also increase the font size to further improve readability. ```{code-cell} ipython3 can_lang_plot_labels = alt.Chart(can_lang).mark_circle(color='black').encode( - x = alt.X("most_at_home",title = ["Language spoken most at home", "(number of Canadian residents)"]), - y = alt.Y("mother_tongue", scale=alt.Scale(zero=False), title = ["Mother tongue", "(number of Canadian residents)"])).configure_axis( - titleFontSize=12) - + x=alt.X( + "most_at_home", + title=["Language spoken most at home", "(number of Canadian residents)"] + ), + y=alt.Y( + "mother_tongue", + scale=alt.Scale(zero=False), + title=["Mother tongue", "(number of Canadian residents)"] + ) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -616,7 +615,7 @@ glue('can_lang_plot_labels', can_lang_plot_labels, display=False) ``` :::{glue:figure} can_lang_plot_labels -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_labels Scatter plot of number of Canadians reporting a language as their mother tongue vs the primary language at home with x and y labels. @@ -628,7 +627,7 @@ Scatter plot of number of Canadians reporting a language as their mother tongue ```{code-cell} ipython3 :tags: ["remove-cell"] import numpy as np -numlang_speakers_max = max(can_lang['mother_tongue']) +numlang_speakers_max=max(can_lang['mother_tongue']) print(numlang_speakers_max) numlang_speakers_min = min(can_lang['mother_tongue']) print(numlang_speakers_min) @@ -644,9 +643,9 @@ much more readable and interpretable now. However, the scatter points themselves some work; most of the 214 data points are bunched up in the lower left-hand side of the visualization. The data is clumped because many more people in Canada speak English or French (the two points in -the upper right corner) than other languages. -In particular, the most common mother tongue language -has {glue:}`numlang_speakers_max` speakers, +the upper right corner) than other languages. +In particular, the most common mother tongue language +has {glue:}`numlang_speakers_max` speakers, while the least common has only {glue:}`numlang_speakers_min`. That's a {glue:}`log_result` -decimal-place difference in the magnitude of these two numbers! @@ -664,7 +663,7 @@ can_lang.loc[(can_lang['language']=='English') | (can_lang['language']=='French' ``` Recall that our question about this data pertains to *all* languages; -so to properly answer our question, +so to properly answer our question, we will need to adjust the scale of the axes so that we can clearly see all of the scatter points. In particular, we will improve the plot by adjusting the horizontal @@ -672,23 +671,32 @@ and vertical axes so that they are on a **logarithmic** (or **log**) scale. Log scaling is useful when your data take both *very large* and *very small* values, because it helps space out small values and squishes larger values together. For example, $\log_{10}(1) = 0$, $\log_{10}(10) = 1$, $\log_{10}(100) = 2$, and $\log_{10}(1000) = 3$; -on the logarithmic scale, +on the logarithmic scale, the values 1, 10, 100, and 1000 are all the same distance apart! -So we see that applying this function is moving big values closer together +So we see that applying this function is moving big values closer together and moving small values farther apart. -Note that if your data can take the value 0, logarithmic scaling may not +Note that if your data can take the value 0, logarithmic scaling may not be appropriate (since `log10(0) = -inf` in Python). There are other ways to transform -the data in such a case, but these are beyond the scope of the book. +the data in such a case, but these are beyond the scope of the book. We can accomplish logarithmic scaling in the `altair` visualization using the argument `type="log"` in the scale functions. ```{code-cell} ipython3 can_lang_plot_log = alt.Chart(can_lang).mark_circle(color='black').encode( - x = alt.X("most_at_home",title = ["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale( type="log"), axis=alt.Axis(tickCount=7)), - y = alt.Y("mother_tongue", title = ["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7))).configure_axis( - titleFontSize=12) - + x=alt.X( + "most_at_home", + title=["Language spoken most at home", "(number of Canadian residents)"], + scale=alt.Scale( type="log"), + axis=alt.Axis(tickCount=7) + ), + y=alt.Y( + "mother_tongue", + title=["Mother tongue", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -697,7 +705,7 @@ glue('can_lang_plot_log', can_lang_plot_log, display=False) ``` :::{glue:figure} can_lang_plot_log -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_log Scatter plot of number of Canadians reporting a language as their mother tongue vs the primary language at home with log adjusted x and y axes. @@ -716,16 +724,16 @@ glue("result", result) ``` -Similar to some of the examples in Chapter {ref}`wrangling`, -we can convert the counts to percentages to give them context +Similar to some of the examples in Chapter {ref}`wrangling`, +we can convert the counts to percentages to give them context and make them easier to understand. -We can do this by dividing the number of people reporting a given language -as their mother tongue or primary language at home -by the number of people who live in Canada and multiplying by 100\%. -For example, -the percentage of people who reported that their mother tongue was English -in the 2016 Canadian census -was {glue:}`english_mother_tongue` / {glue:}`census_popn` $\times$ +We can do this by dividing the number of people reporting a given language +as their mother tongue or primary language at home +by the number of people who live in Canada and multiplying by 100\%. +For example, +the percentage of people who reported that their mother tongue was English +in the 2016 Canadian census +was {glue:}`english_mother_tongue` / {glue:}`census_popn` $\times$ `100` \% = {glue:}`result`\% Below we use `assign` to calculate the percentage of people reporting a given @@ -745,7 +753,7 @@ can_lang[['mother_tongue_percent', 'most_at_home_percent']] ``` Finally, we will edit the visualization to use the percentages we just computed -(and change our axis labels to reflect this change in +(and change our axis labels to reflect this change in units). {numref}`can_lang_plot_percent` displays the final result. @@ -754,8 +762,8 @@ the final result. ```{code-cell} ipython3 can_lang_plot_percent = alt.Chart(can_lang).mark_circle(color='black').encode( - x = alt.X("most_at_home_percent",title = ["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - y = alt.Y("mother_tongue_percent", title = ["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7))).configure_axis( + x=alt.X("most_at_home_percent",title=["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), + y=alt.Y("mother_tongue_percent", title=["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7))).configure_axis( titleFontSize=12) ``` @@ -766,7 +774,7 @@ glue('can_lang_plot_percent', can_lang_plot_percent, display=False) ``` :::{glue:figure} can_lang_plot_percent -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_percent Scatter plot of percentage of Canadians reporting a language as their mother tongue vs the primary language at home. @@ -774,46 +782,46 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton {numref}`can_lang_plot_percent` is the appropriate visualization to use to answer the first question in this section, i.e., -whether there is a relationship between the percentage of people who speak +whether there is a relationship between the percentage of people who speak a language as their mother tongue and the percentage for whom that is the primary language spoken at home. To fully answer the question, we need to use {numref}`can_lang_plot_percent` -to assess a few key characteristics of the data: +to assess a few key characteristics of the data: ```{index} relationship; positive negative none ``` -- **Direction:** if the y variable tends to increase when the x variable increases, then y has a **positive** relationship with x. If - y tends to decrease when x increases, then y has a **negative** relationship with x. If y does not meaningfully increase or decrease - as x increases, then y has **little or no** relationship with x. +- **Direction:** if the y variable tends to increase when the x variable increases, then y has a **positive** relationship with x. If + y tends to decrease when x increases, then y has a **negative** relationship with x. If y does not meaningfully increase or decrease + as x increases, then y has **little or no** relationship with x. ```{index} relationship; strong weak ``` - **Strength:** if the y variable *reliably* increases, decreases, or stays flat as x increases, - then the relationship is **strong**. Otherwise, the relationship is **weak**. Intuitively, + then the relationship is **strong**. Otherwise, the relationship is **weak**. Intuitively, the relationship is strong when the scatter points are close together and look more like a "line" or "curve" than a "cloud." ```{index} relationship; linear nonlinear ``` -- **Shape:** if you can draw a straight line roughly through the data points, the relationship is **linear**. Otherwise, it is **nonlinear**. +- **Shape:** if you can draw a straight line roughly through the data points, the relationship is **linear**. Otherwise, it is **nonlinear**. -In {numref}`can_lang_plot_percent`, we see that -as the percentage of people who have a language as their mother tongue increases, -so does the percentage of people who speak that language at home. +In {numref}`can_lang_plot_percent`, we see that +as the percentage of people who have a language as their mother tongue increases, +so does the percentage of people who speak that language at home. Therefore, there is a **positive** relationship between these two variables. Furthermore, because the points in {numref}`can_lang_plot_percent` are fairly close together, and the points look more like a "line" than a "cloud", -we can say that this is a **strong** relationship. -And finally, because drawing a straight line through these points in +we can say that this is a **strong** relationship. +And finally, because drawing a straight line through these points in {numref}`can_lang_plot_percent` would fit the pattern we observe quite well, we say that the relationship is **linear**. Onto the second part of our exploratory data analysis question! -Recall that we are interested in knowing whether the strength -of the relationship we uncovered +Recall that we are interested in knowing whether the strength +of the relationship we uncovered in {numref}`can_lang_plot_percent` depends on the higher-level language category (Official languages, Aboriginal languages, and non-official, non-Aboriginal languages). @@ -821,24 +829,34 @@ One common way to explore this is to color the data points on the scatter plot we have already created by group. For example, given that we have the higher-level language category for each language recorded in the 2016 Canadian census, we can color the points in -our previous +our previous scatter plot to represent each language's higher-level language category. Here we want to distinguish the values according to the `category` group with which they belong. We can add the argument `color` to the `encode` function, specifying that the `category` column should color the points. Adding this argument will color the points according to their group and add a legend at the side of the -plot. +plot. ```{code-cell} ipython3 -can_lang_plot_category = alt.Chart(can_lang).mark_circle().encode( - x = alt.X("most_at_home_percent", title = ["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - y = alt.Y("mother_tongue_percent", title = ["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - color = "category").configure_axis( - titleFontSize=12) +can_lang_plot_category=alt.Chart(can_lang).mark_circle().encode( + x=alt.X( + "most_at_home_percent", + title=["Language spoken most at home", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + y=alt.Y( + "mother_tongue_percent", + title=["Mother tongue", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + color="category" +).configure_axis(titleFontSize=12) ``` @@ -848,7 +866,7 @@ glue('can_lang_plot_category', can_lang_plot_category, display=False) ``` :::{glue:figure} can_lang_plot_category -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_category Scatter plot of percentage of Canadians reporting a language as their mother tongue vs the primary language at home colored by language category. @@ -856,29 +874,42 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton The legend in {numref}`can_lang_plot_category` -takes up valuable plot area. +takes up valuable plot area. We can improve this by moving the legend title using the `alt.Legend` function with the arguments `legendX`, `legendY` and `direction` -arguments of the `theme` function. -Here we set the `direction` to `"vertical"` so that the legend items remain +arguments of the `theme` function. +Here we set the `direction` to `"vertical"` so that the legend items remain vertically stacked on top of each other. The default `direction` is horizontal, which won't work -not work well for this particular visualization -because the legend labels are quite long +not work well for this particular visualization +because the legend labels are quite long and would run off the page if displayed this way. ```{code-cell} ipython3 can_lang_plot_legend = alt.Chart(can_lang).mark_circle().encode( - x = alt.X("most_at_home_percent",title = ["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"),axis=alt.Axis(tickCount=7)), - y = alt.Y("mother_tongue_percent", title = ["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - color = alt.Color("category", legend=alt.Legend( - orient='none', - legendX=0, legendY=-90, - direction='vertical'))).configure_axis( - titleFontSize=12) - - + x=alt.X( + "most_at_home_percent", + title=["Language spoken most at home", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + y=alt.Y( + "mother_tongue_percent", + title=["Mother tongue", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + color=alt.Color( + "category", + legend=alt.Legend( + orient='none', + legendX=0, + legendY=-90, + direction='vertical' + ) + ) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -887,7 +918,7 @@ glue('can_lang_plot_legend', can_lang_plot_legend, display=False) ``` :::{glue:figure} can_lang_plot_legend -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_legend Scatter plot of percentage of Canadians reporting a language as their mother tongue vs the primary language at home colored by language category with the legend edited. @@ -897,21 +928,21 @@ In {numref}`can_lang_plot_legend`, the points are colored with the default `altair` color palette. But what if you want to use different colors? In Altair, there are many themes available, which can be viewed [here](https://vega.github.io/vega/docs/schemes/) -To change the color scheme, +To change the color scheme, we add the `scheme` argument in the `scale` of the `color` argument in `altair` layer indicating the palette we want to use. ```{index} color palette; color blindness simulator ``` -You can use -this [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/) to check -if your visualizations +You can use +this [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/) to check +if your visualizations are color-blind friendly. Below we pick the `"dark2"` theme, with the result shown in {numref}`can_lang_plot_theme` We also set the `shape` aesthetic mapping to the `category` variable as well; -this makes the scatter point shapes different for each category. This kind of +this makes the scatter point shapes different for each category. This kind of visual redundancy—i.e., conveying the same information with both scatter point color and shape—can further improve the clarity and accessibility of your visualization. @@ -920,16 +951,30 @@ further improve the clarity and accessibility of your visualization. ```{code-cell} ipython3 can_lang_plot_theme = alt.Chart(can_lang).mark_point(filled=True).encode( - x = alt.X("most_at_home_percent",title = ["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - y = alt.Y("mother_tongue_percent", title = "Mother tongue(percentage of Canadian residents)", scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - color = alt.Color("category", legend=alt.Legend( - orient='none', - legendX=0, legendY=-90, - direction='vertical'), - scale=alt.Scale(scheme='dark2')), - shape = "category").configure_axis( - titleFontSize=12) - + x=alt.X( + "most_at_home_percent", + title=["Language spoken most at home", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + y=alt.Y( + "mother_tongue_percent", + title="Mother tongue(percentage of Canadian residents)", + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + color=alt.Color( + "category", + legend=alt.Legend( + orient='none', + legendX=0, + legendY=-90, + direction='vertical' + ), + scale=alt.Scale(scheme='dark2') + ), + shape="category" +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -938,26 +983,26 @@ glue('can_lang_plot_theme', can_lang_plot_theme, display=False) ``` :::{glue:figure} can_lang_plot_theme -:figwidth: 700px +:figwidth: 700px :name: can_lang_plot_theme Scatter plot of percentage of Canadians reporting a language as their mother tongue vs the primary language at home colored by language category with color-blind friendly colors. ::: -From the visualization in {numref}`can_lang_plot_theme`, -we can now clearly see that the vast majority of Canadians reported one of the official languages -as their mother tongue and as the language they speak most often at home. -What do we see when considering the second part of our exploratory question? +From the visualization in {numref}`can_lang_plot_theme`, +we can now clearly see that the vast majority of Canadians reported one of the official languages +as their mother tongue and as the language they speak most often at home. +What do we see when considering the second part of our exploratory question? Do we see a difference in the relationship between languages spoken as a mother tongue and as a primary language -at home across the higher-level language categories? +at home across the higher-level language categories? Based on {numref}`can_lang_plot_theme`, there does not appear to be much of a difference. -For each higher-level language category, -there appears to be a strong, positive, and linear relationship between -the percentage of people who speak a language as their mother tongue -and the percentage who speak it as their primary language at home. -The relationship looks similar regardless of the category. +For each higher-level language category, +there appears to be a strong, positive, and linear relationship between +the percentage of people who speak a language as their mother tongue +and the percentage who speak it as their primary language at home. +The relationship looks similar regardless of the category. Does this mean that this relationship is positive for all languages in the world? And further, can we use this data visualization on its own to predict how many people @@ -966,15 +1011,15 @@ it as their primary language at home? The answer to both these questions is "no!" However, with exploratory data analysis, we can create new hypotheses, ideas, and questions (like the ones at the beginning of this paragraph). Answering those questions often involves doing more complex analyses, and sometimes -even gathering additional data. We will see more of such complex analyses later on in -this book. +even gathering additional data. We will see more of such complex analyses later on in +this book. ### Bar plots: the island landmass data set ```{index} Island landmasses ``` -The `islands.csv` data set contains a list of Earth's landmasses as well as their area (in thousands of square miles) {cite:p}`islandsdata`. +The `islands.csv` data set contains a list of Earth's landmasses as well as their area (in thousands of square miles) {cite:p}`islandsdata`. ```{index} question; visualization ``` @@ -988,10 +1033,10 @@ islands_df = pd.read_csv("data/islands.csv") islands_df ``` -Here, we have a data frame of Earth's landmasses, -and are trying to compare their sizes. -The right type of visualization to answer this question is a bar plot. -In a bar plot, the height of the bar represents the value of a summary statistic +Here, we have a data frame of Earth's landmasses, +and are trying to compare their sizes. +The right type of visualization to answer this question is a bar plot. +In a bar plot, the height of the bar represents the value of a summary statistic (usually a size, count, proportion or percentage). They are particularly useful for comparing summary statistics between different groups of a categorical variable. @@ -1000,13 +1045,13 @@ groups of a categorical variable. ``` We specify that we would like to use a bar plot -via the `mark_bar` function in `altair`. +via the `mark_bar` function in `altair`. The result is shown in {numref}`islands_bar` ```{code-cell} ipython3 islands_bar = alt.Chart(islands_df).mark_bar().encode( - x = "landmass", y = "size") - + x="landmass", y="size" +) ``` ```{code-cell} ipython3 @@ -1015,7 +1060,7 @@ glue('islands_bar', islands_bar, display=False) ``` :::{glue:figure} islands_bar -:figwidth: 700px +:figwidth: 700px :name: islands_bar Bar plot of all Earth's landmasses' size with squished labels. @@ -1035,10 +1080,11 @@ swapping the `x` and `y` variables: ``` ```{code-cell} ipython3 -islands_top12 = islands_df.sort_values(by = "size", ascending=False).iloc[:12] +islands_top12 = islands_df.sort_values(by="size", ascending=False).iloc[:12] islands_bar_sorted = alt.Chart(islands_top12).mark_bar().encode( - x = "size", y = "landmass") + x="size", y="landmass" +) ``` ```{code-cell} ipython3 @@ -1047,30 +1093,28 @@ glue('islands_bar_sorted', islands_bar_sorted, display=True) ``` :::{glue:figure} islands_bar_sorted -:figwidth: 700px +:figwidth: 700px :name: islands_bar_sorted Bar plot of size for Earth's largest 12 landmasses. ::: - - -The plot in {numref}`islands_bar_sorted` is definitely clearer now, -and allows us to answer our question -("are the top 7 largest landmasses continents?") in the affirmative. -But the question could be made clearer from the plot +The plot in {numref}`islands_bar_sorted` is definitely clearer now, +and allows us to answer our question +("are the top 7 largest landmasses continents?") in the affirmative. +But the question could be made clearer from the plot by organizing the bars not by alphabetical order -but by size, and to color them based on whether they are a continent. -The data for this is stored in the `landmass_type` column. -To use this to color the bars, +but by size, and to color them based on whether they are a continent. +The data for this is stored in the `landmass_type` column. +To use this to color the bars, we use the `color` argument to color the bars according to the `landmass_type` -To organize the landmasses by their `size` variable, +To organize the landmasses by their `size` variable, we will use the `altair` `sort` function in encoding for `y` axis to organize the landmasses by their `size` variable, which is encoded on the x-axis. To sort the landmasses by their size(denoted on `x` axis), we use `sort='x'`. This plots the values on `y` axis -in the ascending order of `x` axis values. +in the ascending order of `x` axis values. We do this here so that the largest bar will be closest to the axis line, which is more visually appealing. @@ -1086,15 +1130,15 @@ The default label is the name of the column being mapped to `color`. Here that would be `landmass_type`; however `landmass_type` is not proper English (and so is less readable). Thus we use the `title` argument inside `alt.Color` to change that to "Type" -Finally, we again use the `configure_axis` function +Finally, we again use the `configure_axis` function to change the font size. ```{code-cell} ipython3 islands_plot_sorted = alt.Chart(islands_top12).mark_bar(color='black').encode( - x = alt.X("size",title = "Size (1000 square mi)"), - y = alt.Y("landmass", title = "Landmass", sort='x'), - color = alt.Color("landmass_type", title = "Type")).configure_axis( - titleFontSize=12) + x=alt.X("size",title="Size (1000 square mi)"), + y=alt.Y("landmass", title="Landmass", sort='x'), + color=alt.Color("landmass_type", title="Type") +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -1103,7 +1147,7 @@ glue('islands_plot_sorted', islands_plot_sorted, display=True) ``` :::{glue:figure} islands_plot_sorted -:figwidth: 700px +:figwidth: 700px :name: islands_plot_sorted Bar plot of size for Earth's largest 12 landmasses colored by whether its a continent with clearer axes and labels. @@ -1120,28 +1164,28 @@ making it quite clear that continents are the largest seven landmasses. ```{index} Michelson speed of light ``` -The `morley` data set -contains measurements of the speed of light +The `morley` data set +contains measurements of the speed of light collected in experiments performed in 1879. -Five experiments were performed, -and in each experiment, 20 runs were performed—meaning that -20 measurements of the speed of light were collected +Five experiments were performed, +and in each experiment, 20 runs were performed—meaning that +20 measurements of the speed of light were collected in each experiment {cite:p}`lightdata`. -Because the speed of light is a very large number +Because the speed of light is a very large number (the true value is 299,792.458 km/sec), the data is coded to be the measured speed of light minus 299,000. This coding allows us to focus on the variations in the measurements, which are generally much smaller than 299,000. If we used the full large speed measurements, the variations in the measurements would not be noticeable, making it difficult to study the differences between the experiments. -Note that we convert the `morley` data to a tibble to take advantage of the nicer print output +Note that we convert the `morley` data to a tibble to take advantage of the nicer print output these specialized data frames provide. ```{index} question; visualization ``` -**Question:** Given what we know now about the speed of +**Question:** Given what we know now about the speed of light (299,792.458 kilometres per second), how accurate were each of the experiments? First, we read in the data. @@ -1153,27 +1197,28 @@ morley_df = pd.read_csv("data/morley.csv") ```{index} distribution, altair; histogram ``` -In this experimental data, -Michelson was trying to measure just a single quantitative number -(the speed of light). -The data set contains many measurements of this single quantity. -To tell how accurate the experiments were, -we need to visualize the distribution of the measurements -(i.e., all their possible values and how often each occurs). -We can do this using a *histogram*. -A histogram -helps us visualize how a particular variable is distributed in a data set -by separating the data into bins, -and then using vertical bars to show how many data points fell in each bin. +In this experimental data, +Michelson was trying to measure just a single quantitative number +(the speed of light). +The data set contains many measurements of this single quantity. +To tell how accurate the experiments were, +we need to visualize the distribution of the measurements +(i.e., all their possible values and how often each occurs). +We can do this using a *histogram*. +A histogram +helps us visualize how a particular variable is distributed in a data set +by separating the data into bins, +and then using vertical bars to show how many data points fell in each bin. To create a histogram in `altair` we will use the `mark_bar` geometric -object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `count()`. As usual, +object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `count()`. As usual, let's use the default arguments just to see how things look. ```{code-cell} ipython3 morley_hist = alt.Chart(morley_df).mark_bar().encode( - x = alt.X("Speed"), - y='count()') + x=alt.X("Speed"), + y='count()' +) ``` ```{code-cell} ipython3 @@ -1182,7 +1227,7 @@ glue('morley_hist', morley_hist, display=False) ``` :::{glue:figure} morley_hist -:figwidth: 700px +:figwidth: 700px :name: morley_hist Histogram of Michelson's speed of light data. @@ -1191,39 +1236,37 @@ Histogram of Michelson's speed of light data. ```{index} altair; mark_rule ``` -{numref}`morley_hist` is a great start. -However, -we cannot tell how accurate the measurements are using this visualization +{numref}`morley_hist` is a great start. +However, +we cannot tell how accurate the measurements are using this visualization unless we can see the true value. -In order to visualize the true speed of light, +In order to visualize the true speed of light, we will add a vertical line with the `mark_rule` function. -To draw a vertical line with `mark_rule`, -we need to specify where on the x-axis the line should be drawn. -We can do this by creating a dataframe with just one column with value `792.458`, which is the true value of light speed -minus 299,000 and encoding it in the `x` axis; this ensures it is coded the same way as the +To draw a vertical line with `mark_rule`, +we need to specify where on the x-axis the line should be drawn. +We can do this by creating a dataframe with just one column with value `792.458`, which is the true value of light speed +minus 299,000 and encoding it in the `x` axis; this ensures it is coded the same way as the measurements in the `morley` data frame. -We would also like to fine tune this vertical line, +We would also like to fine tune this vertical line, styling it so that it is dashed and 1 point in thickness. -A point is a measurement unit commonly used with fonts, -and 1 point is about 0.353 mm. -We do this by setting `strokeDash=[3,3]` and `size = 1`, respectively. +A point is a measurement unit commonly used with fonts, +and 1 point is about 0.353 mm. +We do this by setting `strokeDash=[3,3]` and `size = 1`, respectively. Similarly, a horizontal line can be plotted using the `y` axis encoding and the dataframe with one value, which would act as the be the y-intercept -Note that -*vertical lines* are used to denote quantities on the *horizontal axis*, -while *horizontal lines* are used to denote quantities on the *vertical axis*. +Note that +*vertical lines* are used to denote quantities on the *horizontal axis*, +while *horizontal lines* are used to denote quantities on the *vertical axis*. To add the dashed line on top of the histogram, we will use the `+` operator. This concept is also known as layering in altair.(This is covered in the later sections of the chapter). Here, we add the `mark_rule` chart on the `morley_hist` chart of the form `mark_bar` ```{code-cell} ipython3 v_line = alt.Chart(pd.DataFrame({'x': [792.458]})).mark_rule( - strokeDash=[3,3], size=1).encode( - x='x') - + strokeDash=[3,3], size=1 +).encode(x='x') final_plot = morley_hist + v_line - ``` @@ -1233,34 +1276,35 @@ glue('final_plot_viz', final_plot, display=False) ``` :::{glue:figure} final_plot_viz -:figwidth: 700px +:figwidth: 700px :name: final_plot_viz Histogram of Michelson's speed of light data with vertical line indicating true speed of light. ::: -In {numref}`final_plot_viz`, -we still cannot tell which experiments (denoted in the `Expt` column) -led to which measurements; -perhaps some experiments were more accurate than others. -To fully answer our question, -we need to separate the measurements from each other visually. -We can try to do this using a *colored* histogram, -where counts from different experiments are stacked on top of each other -in different colors. -We can create a histogram colored by the `Expt` variable -by adding it to the `color` argument. -We make sure the different colors can be seen -(despite them all sitting on top of each other) -by setting the `opacity` argument in `mark_bar` to `0.5` -to make the bars slightly translucent. +In {numref}`final_plot_viz`, +we still cannot tell which experiments (denoted in the `Expt` column) +led to which measurements; +perhaps some experiments were more accurate than others. +To fully answer our question, +we need to separate the measurements from each other visually. +We can try to do this using a *colored* histogram, +where counts from different experiments are stacked on top of each other +in different colors. +We can create a histogram colored by the `Expt` variable +by adding it to the `color` argument. +We make sure the different colors can be seen +(despite them all sitting on top of each other) +by setting the `opacity` argument in `mark_bar` to `0.5` +to make the bars slightly translucent. ```{code-cell} ipython3 morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( - x = alt.X("Speed"), + x=alt.X("Speed"), y=alt.Y('count()'), - color = "Expt") + color="Expt" +) final_plot_colored = morley_hist_colored + v_line @@ -1272,7 +1316,7 @@ glue('final_plot_colored', final_plot_colored, display=True) ``` :::{glue:figure} final_plot_colored -:figwidth: 700px +:figwidth: 700px :name: final_plot_colored Histogram of Michelson's speed of light data colored by experiment. @@ -1281,27 +1325,28 @@ Histogram of Michelson's speed of light data colored by experiment. ```{index} integer ``` -Alright great, {numref}`final_plot_colored` looks...wait a second! We are not able to distinguish -between different Experiments in the histogram! What is going on here? Well, if you +Alright great, {numref}`final_plot_colored` looks...wait a second! We are not able to distinguish +between different Experiments in the histogram! What is going on here? Well, if you recall from Chapter {ref}`wrangling`, the *data type* you use for each variable can influence how Python and `altair` treats it. Here, we indeed have an issue with the data types in the `morley` data frame. In particular, the `Expt` column is currently an *integer*. But we want to treat it as a -*category*, i.e., there should be one category per type of experiment. +*category*, i.e., there should be one category per type of experiment. ```{index} nominal, altair; :N ``` -To fix this issue we can convert the `Expt` variable into a `nominal`(categorical) type +To fix this issue we can convert the `Expt` variable into a `nominal`(categorical) type variable by adding a suffix `:N`(where `N` stands for nominal type variable) with the `Expt` variable. By doing this, we are ensuring that `altair` will treat this variable as a categorical variable, and the color will be mapped discretely. Here, we also mention `stack=False`, so that the bars are not stacked on top of each other. ```{code-cell} ipython3 morley_hist_categorical = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( - x = alt.X("Speed", bin=alt.Bin(maxbins=50)), + x=alt.X("Speed", bin=alt.Bin(maxbins=50)), y=alt.Y('count()', stack=False), - color = "Expt:N") + color="Expt:N" +) final_plot_categorical = morley_hist_categorical + v_line @@ -1313,14 +1358,14 @@ glue('final_plot_categorical', final_plot_categorical, display=True) ``` :::{glue:figure} final_plot_categorical -:figwidth: 700px +:figwidth: 700px :name: final_plot_categorical Histogram of Michelson's speed of light data colored by experiment as a categorical variable. ::: - + Unfortunately, the attempt to separate out the experiment number visually has created a bit of a mess. All of the colors in {numref}`final_plot_categorical` are blending together, and although it is possible to derive *some* insight from this (e.g., experiments 1 and 3 had some @@ -1333,25 +1378,27 @@ grid of separate histogram plots. ```{index} altair; facet ``` -We use the `facet` function to create a plot +We use the `facet` function to create a plot that has multiple subplots arranged in a grid. -The argument to `facet` specifies the variable(s) used to split the plot +The argument to `facet` specifies the variable(s) used to split the plot into subplots, and how to split them (i.e., into rows or columns). -If the plot is to be split horizontally, into rows, +If the plot is to be split horizontally, into rows, then the `rows` argument is used. -If the plot is to be split vertically, into columns, +If the plot is to be split vertically, into columns, then the `columns` argument is used. -Both the `rows` and `columns` arguments take the column names on which to split the data when creating the subplots. +Both the `rows` and `columns` arguments take the column names on which to split the data when creating the subplots. ```{code-cell} ipython3 -morley_hist = alt.Chart(morley_df).mark_bar(opacity = 0.5).encode( - x = alt.X("Speed", bin=alt.Bin(maxbins=50)), +morley_hist = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( + x=alt.X("Speed", bin=alt.Bin(maxbins=50)), y=alt.Y('count()', stack=False), - color = "Expt:N").properties(height=100, width=300) - -final_plot_facet = (morley_hist + v_line).facet(row = 'Expt:N', data = morley_df) + color="Expt:N" +).properties(height=100, width=300) +final_plot_facet = (morley_hist + v_line).facet( + row='Expt:N', data=morley_df +) ``` ```{code-cell} ipython3 @@ -1360,16 +1407,16 @@ glue('final_plot_facet', final_plot_facet, display=True) ``` :::{glue:figure} final_plot_facet -:figwidth: 700px +:figwidth: 700px :name: final_plot_facet Histogram of Michelson's speed of light data split vertically by experiment. ::: The visualization in {numref}`final_plot_facet` -now makes it quite clear how accurate the different experiments were -with respect to one another. -The most variable measurements came from Experiment 1. +now makes it quite clear how accurate the different experiments were +with respect to one another. +The most variable measurements came from Experiment 1. There the measurements ranged from about 650–1050 km/sec. The least variable measurements came from Experiment 2. There, the measurements ranged from about 750–950 km/sec. @@ -1379,16 +1426,19 @@ The most different experiments still obtained quite similar results! ``` There are two finishing touches to make this visualization even clearer. First and foremost, we need to add informative axis labels -using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. Second, and perhaps more subtly, even though it -is easy to compare the experiments on this plot to one another, it is hard to get a sense +using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. Second, and perhaps more subtly, even though it +is easy to compare the experiments on this plot to one another, it is hard to get a sense of just how accurate all the experiments were overall. For example, how accurate is the value 800 on the plot, relative to the true speed of light? To answer this question, we'll use the assign function to transform our data into a relative measure of accuracy rather than absolute measurements: ```{code-cell} ipython3 morley_rel = morley_df -morley_rel = morley_rel.assign(relative_accuracy = 100 * - ((299000 + morley_df['Speed']) - 299792.458) / (299792.458) ) +morley_rel = morley_rel.assign( + relative_accuracy=( + 100 *((299000 + morley_df['Speed']) - 299792.458) / (299792.458) + ) +) morley_rel ``` @@ -1396,13 +1446,28 @@ morley_rel ```{code-cell} ipython3 v_line = alt.Chart(pd.DataFrame({'x': [0]})).mark_rule( strokeDash=[3,3], size=2).encode( - x='x') -morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( - x = alt.X("relative_accuracy", bin=alt.Bin(maxbins=120), title = "Relative Accuracy (%)"), - y=alt.Y('count()', stack=False, title = "# Measurements"), - color = alt.Color("Expt:N", title = "Experiment ID")).properties(height=100, width= 400) + x='x' +) -final_plot_relative = (morley_hist + v_line).facet(row='Expt:N', data=morley_rel) +morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( + x=alt.X( + "relative_accuracy", + bin=alt.Bin(maxbins=120), + title="Relative Accuracy (%)" + ), + y=alt.Y( + 'count()', + stack=False, + title="# Measurements" + ), + color=alt.Color( + "Expt:N", title="Experiment ID" + ) +).properties(height=100, width= 400) + +final_plot_relative = (morley_hist + v_line).facet( + row='Expt:N', data=morley_rel +) ``` @@ -1412,13 +1477,13 @@ glue('final_plot_relative', final_plot_relative, display=True) ``` :::{glue:figure} final_plot_relative -:figwidth: 700px +:figwidth: 700px :name: final_plot_relative Histogram of relative accuracy split vertically by experiment with clearer axes and labels ::: -Wow, impressive! These measurements of the speed of light from 1879 had errors around *0.05%* of the true speed. {numref}`final_plot_relative` shows you that even though experiments 2 and 5 were perhaps the most accurate, all of the experiments did quite an +Wow, impressive! These measurements of the speed of light from 1879 had errors around *0.05%* of the true speed. {numref}`final_plot_relative` shows you that even though experiments 2 and 5 were perhaps the most accurate, all of the experiments did quite an admirable job given the technology available at the time. #### Choosing a binwidth for histograms @@ -1428,25 +1493,25 @@ Naturally, this is not always the right number to use. You can set the number of bins yourself by using the `maxbins` argument in the `mark_bar` geometric object. -But what number of bins is the right one to use? +But what number of bins is the right one to use? Unfortunately there is no hard rule for what the right bin number -or width is. It depends entirely on your problem; the *right* number of bins -or bin width is -the one that *helps you answer the question* you asked. -Choosing the correct setting for your problem +or width is. It depends entirely on your problem; the *right* number of bins +or bin width is +the one that *helps you answer the question* you asked. +Choosing the correct setting for your problem is something that commonly takes iteration. It's usually a good idea to try out several `maxbins` to see which one most clearly captures your data in the context of the question you want to answer. -To get a sense for how different bin affect visualizations, +To get a sense for how different bin affect visualizations, let's experiment with the histogram that we have been working on in this section. In {numref}`final_plot_max_bins`, -we compare the default setting with three other histograms where we set the +we compare the default setting with three other histograms where we set the `maxbins` to 200, 70 and 5. -In this case, we can see that both the default number of bins +In this case, we can see that both the default number of bins and the `maxbins=70` of are effective for helping answer our question. On the other hand, the `maxbins=200` and `maxbins=5` are too small and too big, respectively. @@ -1457,32 +1522,77 @@ On the other hand, the `maxbins=200` and `maxbins=5` are too small and too big, :tags: ["remove-cell"] morley_hist_default = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( - x = alt.X("relative_accuracy", title = "Relative Accuracy (%)"), - y=alt.Y('count()', stack=False, title = "# Measurements"), - color = alt.Color("Expt:N", title = "Experiment ID")).properties(height=100, width=400) + x=alt.X( + "relative_accuracy", + title="Relative Accuracy (%)" + ), + y=alt.Y( + 'count()', + stack=False, + title="# Measurements" + ), + color=alt.Color( + "Expt:N", + title="Experiment ID" + ) +).properties(height=100, width=400) morley_hist_200 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( - x = alt.X("relative_accuracy", bin=alt.Bin(maxbins=200), title = "Relative Accuracy (%)"), - y=alt.Y('count()', stack=False, title = "# Measurements"), - color = alt.Color("Expt:N", title = "Experiment ID")).properties(height=100, width=400) + x=alt.X( + "relative_accuracy", + bin=alt.Bin(maxbins=200), + title="Relative Accuracy (%)" + ), + y=alt.Y( + "count()", + stack=False, + title="# Measurements" + ), + color=alt.Color( + "Expt:N", title="Experiment ID" + ) +).properties(height=100, width=400) + morley_hist_70 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( - x = alt.X("relative_accuracy", bin=alt.Bin(maxbins=70), title = "Relative Accuracy (%)"), - y=alt.Y('count()', stack=False, title = "# Measurements"), - color = alt.Color("Expt:N", title = "Experiment ID")).properties(height=100, width=400) + x=alt.X( + "relative_accuracy", + bin=alt.Bin(maxbins=70), + title="Relative Accuracy (%)" + ), + y=alt.Y( + "count()", + stack=False, + title="# Measurements" + ), + color=alt.Color( + "Expt:N", + title="Experiment ID" + ) +).properties(height=100, width=400) morley_hist_5 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( - x = alt.X("relative_accuracy", bin=alt.Bin(maxbins=5), title = "Relative Accuracy (%)"), - y=alt.Y('count()', stack=False, title = "# Measurements"), - color = alt.Color("Expt:N", title = "Experiment ID")).properties(height=100, width=300) - - - - - -final_plot_max_bins = ((morley_hist_default + v_line).facet(row='Expt:N', data=morley_rel, title = "default maxbins") | (morley_hist_200 + v_line).facet(row='Expt:N', data=morley_rel, title = "maxBins=200")) & ((morley_hist_70 + v_line).facet(row='Expt:N', data=morley_rel, title = "maxBins=70") | (morley_hist_5 + v_line).facet(row='Expt:N', data=morley_rel, title = "maxBins=5")) - - - + x=alt.X( + "relative_accuracy", + bin=alt.Bin(maxbins=5), + title="Relative Accuracy (%)" + ), + y=alt.Y( + "count()", + stack=False, + title="# Measurements" + ), + color=alt.Color( + "Expt:N", + title="Experiment ID" + ) +).properties(height=100, width=300) + +final_plot_max_bins = ( + (morley_hist_default + v_line).facet(row='Expt:N', data=morley_rel, title="default maxbins") | + (morley_hist_200 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=200")) & + ((morley_hist_70 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=70") | + (morley_hist_5 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=5") +) ``` ```{code-cell} ipython3 @@ -1491,7 +1601,7 @@ glue('final_plot_max_bins', final_plot_max_bins, display=True) ``` :::{glue:figure} final_plot_max_bins -:figwidth: 700px +:figwidth: 700px :name: final_plot_max_bins Effect of varying number of max bins on histograms. @@ -1502,26 +1612,28 @@ Effect of varying number of max bins on histograms. ```{index} altair; + ``` -One of the powerful features of `altair` is that you +One of the powerful features of `altair` is that you can continue to iterate on a single plot object, adding and refining one layer at a time. If you stored your plot as a named object -using the assignment symbol (`=`), you can +using the assignment symbol (`=`), you can add to it using the `+` operator. -For example, if we wanted to add a vertical line to the last plot we created (`morley_hist`), +For example, if we wanted to add a vertical line to the last plot we created (`morley_hist`), we can use the `+` operator to add a vertical line chart layer with the `mark_rule` function. The result is shown in {numref}`morley_hist_layer`. ```{code-cell} ipython3 morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( - x = alt.X("Speed"), + x=alt.X("Speed"), y=alt.Y('count()'), - color = "Expt:N") - + color="Expt:N" +) + v_line = alt.Chart(pd.DataFrame({'x': [792.458]})).mark_rule( strokeDash=[3,3], size=1).encode( - x='x') -morley_hist_layer = morley_hist_colored + v_line - + x='x' +) + +morley_hist_layer = morley_hist_colored + v_line ``` ```{code-cell} ipython3 @@ -1530,7 +1642,7 @@ glue('morley_hist_layer', morley_hist_layer, display=True) ``` :::{glue:figure} morley_hist_layer -:figwidth: 700px +:figwidth: 700px :name: morley_hist_layer Histogram of Michelson's speed of light data colored by experiment with layered vertical line. @@ -1541,10 +1653,11 @@ We can also add a title to the chart by specifying `title` argument in the `alt. ```{code-cell} ipython3 -morley_hist_title = alt.Chart(morley_df, title = "Histogram of Michelson's speed of light data colored by experiment").mark_bar(opacity=0.5).encode( - x = alt.X("Speed"), +morley_hist_title=alt.Chart(morley_df, title="Histogram of Michelson's speed of light data colored by experiment").mark_bar(opacity=0.5).encode( + x=alt.X("Speed"), y=alt.Y('count()'), - color = "Expt:N") + color="Expt:N" +) ``` @@ -1554,15 +1667,15 @@ glue('morley_hist_title', morley_hist_title, display=True) ``` :::{glue:figure} morley_hist_title -:figwidth: 700px +:figwidth: 700px :name: morley_hist_title Histogram of Michelson's speed of light data colored with title ::: -> **Note:** Good visualization titles clearly communicate -> the take home message to the audience. Typically, +> **Note:** Good visualization titles clearly communicate +> the take home message to the audience. Typically, > that is the answer to the question you posed before making the visualization. ## Explaining the visualization @@ -1575,24 +1688,24 @@ conclusion. For example, you could use an exploratory visualization in the opening of the presentation to motivate your choice of a more detailed data analysis / model, a visualization of the results of your analysis to show what your analysis has uncovered, or even one at the end of a presentation to help -suggest directions for future work. +suggest directions for future work. ```{index} visualization; explanation ``` Regardless of where it appears, a good way to discuss your visualization is as -a story: +a story: -1) Establish the setting and scope, and describe why you did what you did. +1) Establish the setting and scope, and describe why you did what you did. 2) Pose the question that your visualization answers. Justify why the question is important to answer. -3) Answer the question using your visualization. Make sure you describe *all* aspects of the visualization (including describing the axes). But you +3) Answer the question using your visualization. Make sure you describe *all* aspects of the visualization (including describing the axes). But you can emphasize different aspects based on what is important to answer your question: - **trends (lines):** Does a line describe the trend well? If so, the trend is *linear*, and if not, the trend is *nonlinear*. Is the trend increasing, decreasing, or neither? Is there a periodic oscillation (wiggle) in the trend? Is the trend noisy (does the line "jump around" a lot) or smooth? - **distributions (scatters, histograms):** How spread out are the data? Where are they centered, roughly? Are there any obvious "clusters" or "subgroups", which would be visible as multiple bumps in the histogram? - **distributions of two variables (scatters):** Is there a clear / strong relationship between the variables (points fall in a distinct pattern), a weak one (points fall in a pattern but there is some noise), or no discernible relationship (the data are too noisy to make any conclusion)? - - **amounts (bars):** How large are the bars relative to one another? Are there patterns in different groups of bars? + - **amounts (bars):** How large are the bars relative to one another? Are there patterns in different groups of bars? 4) Summarize your findings, and use them to motivate whatever you will discuss next. Below are two examples of how one might take these four steps in describing the example visualizations that appeared earlier in this chapter. @@ -1601,7 +1714,7 @@ Each of the steps is denoted by its numeral in parentheses, e.g. (3). ```{index} Mauna Loa ``` -**Mauna Loa Atmospheric CO$_{\text{2}}$ Measurements:** (1) Many +**Mauna Loa Atmospheric CO$_{\text{2}}$ Measurements:** (1) Many current forms of energy generation and conversion—from automotive engines to natural gas power plants—rely on burning fossil fuels and produce greenhouse gases, typically primarily carbon dioxide (CO$_{\text{2}}$), as a @@ -1653,7 +1766,7 @@ such as file size/type limitations (e.g., if you are submitting your visualization as part of a conference paper or to a poster printing shop) and where it will be displayed (e.g., online, in a paper, on a poster, on a billboard, in talk slides). Generally speaking, images come in two flavors: -*raster* formats +*raster* formats and *vector* formats. ```{index} raster graphics; file types @@ -1666,22 +1779,22 @@ perfectly re-created when loading and displaying, with the hope that the change is not noticeable. *Lossless* formats, on the other hand, allow a perfect display of the original image. -- *Common file types:* - +- *Common file types:* + - [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) (`.png`): lossless, usually used for plots / line drawings - + - *Open-source software:* [GIMP](https://www.gimp.org/) ```{index} vector graphics; file types ``` -**Vector** images are represented as a collection of mathematical -objects (lines, surfaces, shapes, curves). When the computer displays the image, it +**Vector** images are represented as a collection of mathematical +objects (lines, surfaces, shapes, curves). When the computer displays the image, it redraws all of the elements using their mathematical formulas. -- *Common file types:* - - [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) (`.svg`): general-purpose use - +- *Common file types:* + - [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) (`.svg`): general-purpose use + - *Open-source software:* [Inkscape](https://inkscape.org/) Raster and vector images have opposing advantages and disadvantages. A raster @@ -1693,7 +1806,7 @@ computer has to draw all the elements each time it is displayed. For example, if you have a scatter plot with 1 million points stored as an SVG file, it may take your computer some time to open the image. On the other hand, you can zoom into / scale up vector graphics as much as you like without the image looking -bad, while raster images eventually start to look "pixelated." +bad, while raster images eventually start to look "pixelated." ```{index} PDF ``` @@ -1703,22 +1816,22 @@ bad, while raster images eventually start to look "pixelated." > **Note:** The portable document format [PDF](https://en.wikipedia.org/wiki/PDF) (`.pdf`) is commonly used to > store *both* raster and vector formats. If you try to open a PDF and it's taking a long time -> to load, it may be because there is a complicated vector graphics image that your computer is rendering. +> to load, it may be because there is a complicated vector graphics image that your computer is rendering. -Let's learn how to save plot images to these different file formats using a -scatter plot of -the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) +Let's learn how to save plot images to these different file formats using a +scatter plot of +the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) {cite:p}`faithfuldata`, shown in {numref}`faithful_scatter_labels` Now that we have a named `altair` plot object, we can use the `chart.save` function -to save a file containing this image. -`chart.save` works by taking the path to the directory where you would like to save the file +to save a file containing this image. +`chart.save` works by taking the path to the directory where you would like to save the file (e.g., `img/filename.png` to save a file named `filename` to the `img` directory), The kind of image to save is specified by the file extension. -For example, +For example, to create a PNG image file, we specify that the file extension is `.png`. -Below we demonstrate how to save PNG and SVG file types +Below we demonstrate how to save PNG and SVG file types for the `faithful_scater_labels` plot: ```{code-cell} ipython3 @@ -1769,7 +1882,7 @@ svg_size = os.path.getsize("data/faithful_plot.svg")/1000000 Take a look at the file sizes in {numref}`png-vs-svg-table` Wow, that's quite a difference! Notice that for such a simple plot with few graphical elements (points), the vector graphics format (SVG) is over 100 times -smaller than the uncompressed raster images. +smaller than the uncompressed raster images. In {numref}`png-vs-svg`, we also show what the images look like when we zoom in to a rectangle with only 3 data points. @@ -1792,8 +1905,8 @@ Zoomed in `faithful`, raster (PNG, left) and vector (SVG, right) formats. ## Exercises -Practice exercises for the material covered in this chapter -can be found in the accompanying +Practice exercises for the material covered in this chapter +can be found in the accompanying [worksheets repository](https://github.com/UBC-DSCI/data-science-a-first-intro-worksheets#readme) in the "Effective data visualization" row. You can launch an interactive version of the worksheet in your browser by clicking the "launch binder" button. @@ -1814,7 +1927,7 @@ and guidance that the worksheets provide will function as intended. a wealth of information on designing effective visualizations. It is not specific to any particular programming language or library. If you want to improve your visualization skills, this is the next place to look. -- The [dates and times](https://wesmckinney.com/book/time-series.html){cite:p}`mckinney2012python` +- The [dates and times](https://wesmckinney.com/book/time-series.html){cite:p}`mckinney2012python` chapter is where you should look if you want to learn about `date` and `time`, including how to create them, and how to use them to effectively handle durations, etc ## References From 3846886917d631506933921b28acbbf4129c462d Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Sat, 24 Dec 2022 22:40:49 -0800 Subject: [PATCH 02/16] update viz chapter --- source/viz.md | 139 +++++++++++++++++++++++++++++--------------------- 1 file changed, 81 insertions(+), 58 deletions(-) diff --git a/source/viz.md b/source/viz.md index 34933069..624e7c8a 100644 --- a/source/viz.md +++ b/source/viz.md @@ -321,7 +321,7 @@ glue('co2_line', co2_line, display=False) ``` > **Note:** We can change the size of the point and color of the plot by specifying -> `mark_point(size=10, color='black')` +> `mark_point(size=10, color="black")` :::{glue:figure} co2_line :figwidth: 700px @@ -352,11 +352,10 @@ visual noise to remove. But there are a few things we must do to improve clarity, such as adding informative axis labels and making the font a more readable size. To add axis labels, we use the `title` argument along with `alt.X` and `alt.Y` functions. To change the font size, we use the `configure_axis` function with the -`titleFontSize` argument. We also set the color of the line to be black as it -has a stronger contrast with the background than the default blue color. +`titleFontSize` argument. ```{code-cell} ipython3 -co2_line_labels = alt.Chart(co2_df).mark_line(color='black').encode( +co2_line_labels = alt.Chart(co2_df).mark_line().encode( x=alt.X("date_measured", title ="Year"), y=alt.Y("ppm", scale=alt.Scale(zero=False), title="Atmospheric CO2 (ppm)") ).configure_axis( @@ -376,7 +375,7 @@ glue('co2_line_labels', co2_line_labels, display=False) Line plot of atmospheric concentration of CO$_{2}$ over time with clearer axes and labels. ::: -> **Note:** The `configure_` function in `altair` is complex and supports many other functionalities, which can be viewed [here](https://altair-viz.github.io/user_guide/configuration.html) +> **Note:** The `configure_` function in `altair` supports many other functionalities, which can be viewed [here](https://altair-viz.github.io/user_guide/configuration.html) ```{index} altair; alt.Scale ``` @@ -396,9 +395,18 @@ to specify the upper and lower bounds to limit the axis. ```{code-cell} ipython3 -co2_line_scale = alt.Chart(co2_df).mark_line(color='black', clip=True).encode( - x=alt.X("date_measured", title="Measurement Date", axis=alt.Axis(tickCount=4), scale=alt.Scale(domain=['1990', '1994'])), - y=alt.Y("ppm", scale=alt.Scale(zero=False), title="Atmospheric CO2 (ppm)") +co2_line_scale = alt.Chart(co2_df).mark_line(clip=True).encode( + x=alt.X( + "date_measured", + title="Measurement Date", + axis=alt.Axis(tickCount=4), + scale=alt.Scale(domain=['1990', '1994']) + ), + y=alt.Y( + "ppm", + scale=alt.Scale(zero=False), + title="Atmospheric CO2 (ppm)" + ) ).configure_axis( titleFontSize=12 ) @@ -472,12 +480,14 @@ So a scatter plot is likely to be the most appropriate visualization. Let's create a scatter plot using the `altair` package with the `waiting` variable on the horizontal axis, the `eruptions` variable on the vertical axis, and the `mark_point` geometric object. +By default, `altair` draws only the outline of each point. If we would +like to fill them in, we pass the argument `filled=True` to `mark_point`. The result is shown in {numref}`faithful_scatter`. ```{code-cell} ipython3 -faithful_scatter = alt.Chart(faithful).mark_point(color='black', filled=True).encode( +faithful_scatter = alt.Chart(faithful).mark_point(filled=True).encode( x="waiting", y="eruptions" ) @@ -506,7 +516,7 @@ labels and make the font more readable: ```{code-cell} ipython3 -faithful_scatter_labels = alt.Chart(faithful).mark_circle(color='black').encode( +faithful_scatter_labels = alt.Chart(faithful).mark_circle().encode( x=alt.X("waiting", title="Waiting Time (mins)"), y=alt.Y("eruptions", title="Eruption Duration (mins)") ) @@ -549,7 +559,7 @@ non-official and non-Aboriginal languages)? To get started, we will read and inspect the data: ```{code-cell} ipython3 -can_lang = pd.read_csv("data/can_lang.csv") +can_lang = pd.read_csv("data/can_lang.csv") ``` ```{code-cell} ipython3 @@ -564,7 +574,7 @@ We will begin with a scatter plot of the `mother_tongue` and `most_at_home` colu The resulting plot is shown in {numref}`can_lang_plot` ```{code-cell} ipython3 -can_lang_plot = alt.Chart(can_lang).mark_circle(color='black').encode( +can_lang_plot = alt.Chart(can_lang).mark_circle().encode( x="most_at_home", y="mother_tongue" ) @@ -596,7 +606,7 @@ We should also increase the font size to further improve readability. ```{code-cell} ipython3 -can_lang_plot_labels = alt.Chart(can_lang).mark_circle(color='black').encode( +can_lang_plot_labels = alt.Chart(can_lang).mark_circle().encode( x=alt.X( "most_at_home", title=["Language spoken most at home", "(number of Canadian residents)"] @@ -656,7 +666,9 @@ to Canada's two official languages by filtering the data: ``` ```{code-cell} ipython3 -can_lang.loc[(can_lang['language']=='English') | (can_lang['language']=='French')] +can_lang.loc[ + (can_lang['language']=='English') | (can_lang['language']=='French') +] ``` ```{index} logarithmic scale, altair; logarithmic scaling @@ -683,11 +695,11 @@ We can accomplish logarithmic scaling in the `altair` visualization using the argument `type="log"` in the scale functions. ```{code-cell} ipython3 -can_lang_plot_log = alt.Chart(can_lang).mark_circle(color='black').encode( +can_lang_plot_log = alt.Chart(can_lang).mark_circle().encode( x=alt.X( "most_at_home", title=["Language spoken most at home", "(number of Canadian residents)"], - scale=alt.Scale( type="log"), + scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), y=alt.Y( @@ -746,8 +758,10 @@ you can clearly see the mutated output from the table. ``` ```{code-cell} ipython3 -can_lang = can_lang.assign(mother_tongue_percent = (can_lang['mother_tongue'] / 35151728) * 100, - most_at_home_percent = (can_lang['most_at_home'] / 35151728) * 100) +can_lang = can_lang.assign( + mother_tongue_percent=(can_lang['mother_tongue']/35151728) * 100, + most_at_home_percent=(can_lang['most_at_home']/35151728) * 100 +) can_lang[['mother_tongue_percent', 'most_at_home_percent']] ``` @@ -761,11 +775,20 @@ the final result. ```{code-cell} ipython3 -can_lang_plot_percent = alt.Chart(can_lang).mark_circle(color='black').encode( - x=alt.X("most_at_home_percent",title=["Language spoken most at home", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7)), - y=alt.Y("mother_tongue_percent", title=["Mother tongue", "(number of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7))).configure_axis( - titleFontSize=12) - +can_lang_plot_percent = alt.Chart(can_lang).mark_circle().encode( + x=alt.X( + "most_at_home_percent", + title=["Language spoken most at home", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ), + y=alt.Y( + "mother_tongue_percent", + title=["Mother tongue", "(number of Canadian residents)"], + scale=alt.Scale(type="log"), + axis=alt.Axis(tickCount=7) + ) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -874,15 +897,15 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton The legend in {numref}`can_lang_plot_category` -takes up valuable plot area. +adds a fair bit of horizontal space. We can improve this by moving the legend title using the `alt.Legend` function with the arguments `legendX`, `legendY` and `direction` arguments of the `theme` function. Here we set the `direction` to `"vertical"` so that the legend items remain -vertically stacked on top of each other. The default `direction` is horizontal, which won't work -not work well for this particular visualization -because the legend labels are quite long -and would run off the page if displayed this way. +vertically stacked on top of each other. The default `direction` is horizontal, which works well for many cases, but +for this particular visualization +because the legend labels are quite long, it is a bit cleaner if we move the +legend above the plot instead. @@ -925,8 +948,8 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton ::: In {numref}`can_lang_plot_legend`, the points are colored with -the default `altair` color palette. But what if you want to use different -colors? In Altair, there are many themes available, which can be viewed [here](https://vega.github.io/vega/docs/schemes/) +the default `altair` color palette. This is an appropriate choice for most situations. If you want to use different +colors? In Altair, there are many themes available, which can be viewed [in the documentation](https://vega.github.io/vega/docs/schemes/) To change the color scheme, we add the `scheme` argument in the `scale` of the `color` argument in `altair` layer indicating the palette we want to use. @@ -1076,11 +1099,11 @@ the `sort_values` function followed by the `iloc` property. Then to help us mak space, we'll use horizontal bars instead of vertical ones. We do this by swapping the `x` and `y` variables: -```{index} pandas.DataFrame; sort_values, pandas.DataFrame; iloc[] +```{index} pandas.DataFrame; nlargest ``` ```{code-cell} ipython3 -islands_top12 = islands_df.sort_values(by="size", ascending=False).iloc[:12] +islands_top12 = islands_df.nlargest(12) islands_bar_sorted = alt.Chart(islands_top12).mark_bar().encode( x="size", y="landmass" @@ -1102,7 +1125,7 @@ Bar plot of size for Earth's largest 12 landmasses. The plot in {numref}`islands_bar_sorted` is definitely clearer now, and allows us to answer our question -("are the top 7 largest landmasses continents?") in the affirmative. +("Which are the top 7 largest landmasses continents?") in the affirmative. But the question could be made clearer from the plot by organizing the bars not by alphabetical order but by size, and to color them based on whether they are a continent. @@ -1134,7 +1157,7 @@ Finally, we again use the `configure_axis` function to change the font size. ```{code-cell} ipython3 -islands_plot_sorted = alt.Chart(islands_top12).mark_bar(color='black').encode( +islands_plot_sorted = alt.Chart(islands_top12).mark_bar().encode( x=alt.X("size",title="Size (1000 square mi)"), y=alt.Y("landmass", title="Landmass", sort='x'), color=alt.Color("landmass_type", title="Type") @@ -1217,13 +1240,13 @@ let's use the default arguments just to see how things look. ```{code-cell} ipython3 morley_hist = alt.Chart(morley_df).mark_bar().encode( x=alt.X("Speed"), - y='count()' + y="count()" ) ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('morley_hist', morley_hist, display=False) +glue("morley_hist", morley_hist, display=False) ``` :::{glue:figure} morley_hist @@ -1262,9 +1285,9 @@ while *horizontal lines* are used to denote quantities on the *vertical axis*. To add the dashed line on top of the histogram, we will use the `+` operator. This concept is also known as layering in altair.(This is covered in the later sections of the chapter). Here, we add the `mark_rule` chart on the `morley_hist` chart of the form `mark_bar` ```{code-cell} ipython3 -v_line = alt.Chart(pd.DataFrame({'x': [792.458]})).mark_rule( +v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( strokeDash=[3,3], size=1 -).encode(x='x') +).encode(x="x") final_plot = morley_hist + v_line ``` @@ -1272,7 +1295,7 @@ final_plot = morley_hist + v_line ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_viz', final_plot, display=False) +glue("final_plot_viz", final_plot, display=False) ``` :::{glue:figure} final_plot_viz @@ -1344,7 +1367,7 @@ and the color will be mapped discretely. Here, we also mention `stack=False`, so ```{code-cell} ipython3 morley_hist_categorical = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed", bin=alt.Bin(maxbins=50)), - y=alt.Y('count()', stack=False), + y=alt.Y("count()", stack=False), color="Expt:N" ) @@ -1392,12 +1415,12 @@ Both the `rows` and `columns` arguments take the column names on which to split morley_hist = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed", bin=alt.Bin(maxbins=50)), - y=alt.Y('count()', stack=False), + y=alt.Y("count()", stack=False), color="Expt:N" ).properties(height=100, width=300) final_plot_facet = (morley_hist + v_line).facet( - row='Expt:N', data=morley_df + row="Expt:N", data=morley_df ) ``` @@ -1436,7 +1459,7 @@ To answer this question, we'll use the assign function to transform our data int morley_rel = morley_df morley_rel = morley_rel.assign( relative_accuracy=( - 100 *((299000 + morley_df['Speed']) - 299792.458) / (299792.458) + 100 *((299000 + morley_df["Speed"]) - 299792.458) / (299792.458) ) ) @@ -1444,9 +1467,9 @@ morley_rel ``` ```{code-cell} ipython3 -v_line = alt.Chart(pd.DataFrame({'x': [0]})).mark_rule( +v_line = alt.Chart(pd.DataFrame({"x": [0]})).mark_rule( strokeDash=[3,3], size=2).encode( - x='x' + x="x" ) morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( @@ -1456,7 +1479,7 @@ morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( title="Relative Accuracy (%)" ), y=alt.Y( - 'count()', + "count()", stack=False, title="# Measurements" ), @@ -1466,14 +1489,14 @@ morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( ).properties(height=100, width= 400) final_plot_relative = (morley_hist + v_line).facet( - row='Expt:N', data=morley_rel + row="Expt:N", data=morley_rel ) ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_relative', final_plot_relative, display=True) +glue("final_plot_relative", final_plot_relative, display=True) ``` :::{glue:figure} final_plot_relative @@ -1527,7 +1550,7 @@ morley_hist_default = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( title="Relative Accuracy (%)" ), y=alt.Y( - 'count()', + "count()", stack=False, title="# Measurements" ), @@ -1588,16 +1611,16 @@ morley_hist_5 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( ).properties(height=100, width=300) final_plot_max_bins = ( - (morley_hist_default + v_line).facet(row='Expt:N', data=morley_rel, title="default maxbins") | - (morley_hist_200 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=200")) & - ((morley_hist_70 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=70") | - (morley_hist_5 + v_line).facet(row='Expt:N', data=morley_rel, title="maxBins=5") + (morley_hist_default + v_line).facet(row="Expt:N", data=morley_rel, title="default maxbins") | + (morley_hist_200 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=200")) & + ((morley_hist_70 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=70") | + (morley_hist_5 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=5") ) ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_max_bins', final_plot_max_bins, display=True) +glue("final_plot_max_bins", final_plot_max_bins, display=True) ``` :::{glue:figure} final_plot_max_bins @@ -1624,13 +1647,13 @@ The result is shown in {numref}`morley_hist_layer`. ```{code-cell} ipython3 morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed"), - y=alt.Y('count()'), + y=alt.Y("count()"), color="Expt:N" ) -v_line = alt.Chart(pd.DataFrame({'x': [792.458]})).mark_rule( +v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( strokeDash=[3,3], size=1).encode( - x='x' + x="x" ) morley_hist_layer = morley_hist_colored + v_line @@ -1638,7 +1661,7 @@ morley_hist_layer = morley_hist_colored + v_line ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('morley_hist_layer', morley_hist_layer, display=True) +glue("morley_hist_layer", morley_hist_layer, display=True) ``` :::{glue:figure} morley_hist_layer From 7aa425ea3fae1c3e3ffa1f5f641de8e9cca50cdf Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Sat, 24 Dec 2022 22:58:14 -0800 Subject: [PATCH 03/16] updating the viz chapter --- source/viz.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/source/viz.md b/source/viz.md index 624e7c8a..c3c616e0 100644 --- a/source/viz.md +++ b/source/viz.md @@ -1202,8 +1202,6 @@ This coding allows us to focus on the variations in the measurements, which are much smaller than 299,000. If we used the full large speed measurements, the variations in the measurements would not be noticeable, making it difficult to study the differences between the experiments. -Note that we convert the `morley` data to a tibble to take advantage of the nicer print output -these specialized data frames provide. ```{index} question; visualization ``` @@ -1484,7 +1482,8 @@ morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( title="# Measurements" ), color=alt.Color( - "Expt:N", title="Experiment ID" + "Expt:N", + title="Experiment ID" ) ).properties(height=100, width= 400) @@ -1511,7 +1510,7 @@ admirable job given the technology available at the time. #### Choosing a binwidth for histograms -When you create a histogram in `altair`, the default number of bins used is 30. +When you create a histogram in `altair`, by default, it tries to choose a reasonable number of bins. Naturally, this is not always the right number to use. You can set the number of bins yourself by using the `maxbins` argument in the `mark_bar` geometric object. @@ -1535,7 +1534,7 @@ In {numref}`final_plot_max_bins`, we compare the default setting with three other histograms where we set the `maxbins` to 200, 70 and 5. In this case, we can see that both the default number of bins -and the `maxbins=70` of are effective for helping answer our question. +and the `maxbins=70` of are effective for helping to answer our question. On the other hand, the `maxbins=200` and `maxbins=5` are too small and too big, respectively. @@ -1630,7 +1629,7 @@ glue("final_plot_max_bins", final_plot_max_bins, display=True) Effect of varying number of max bins on histograms. ::: -#### Adding layers to a `altair` plot object {-} +#### Adding layers to a `altair` plot object {+} ```{index} altair; + ``` @@ -1652,9 +1651,8 @@ morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( ) v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( - strokeDash=[3,3], size=1).encode( - x="x" -) + strokeDash=[3,3], size=1 +).encode(x="x") morley_hist_layer = morley_hist_colored + v_line ``` @@ -1678,7 +1676,7 @@ We can also add a title to the chart by specifying `title` argument in the `alt. ```{code-cell} ipython3 morley_hist_title=alt.Chart(morley_df, title="Histogram of Michelson's speed of light data colored by experiment").mark_bar(opacity=0.5).encode( x=alt.X("Speed"), - y=alt.Y('count()'), + y=alt.Y("count()"), color="Expt:N" ) @@ -1702,7 +1700,7 @@ Histogram of Michelson's speed of light data colored with title > that is the answer to the question you posed before making the visualization. ## Explaining the visualization -#### *Tell a story* {-} +#### *Tell a story* Typically, your visualization will not be shown entirely on its own, but rather it will be part of a larger presentation. Further, visualizations can provide From 5ed3d0da4974ac3d5942c2e819deeecc2419a16b Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Tue, 27 Dec 2022 10:04:51 -0800 Subject: [PATCH 04/16] comments addressed through faithful dataset --- source/viz.md | 59 ++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/source/viz.md b/source/viz.md index c3c616e0..80b6ba80 100644 --- a/source/viz.md +++ b/source/viz.md @@ -47,7 +47,7 @@ By the end of the chapter, readers will be able to do the following: - Use `chart.save()` to save visualizations in `.png` and `.svg` format. ## Choosing the visualization -#### *Ask a question, and answer it* {-} +#### *Ask a question, and answer it* ```{index} question; visualization ``` @@ -74,7 +74,7 @@ As with most coding tasks, it is totally fine (and quite common) to make mistakes and iterate a few times before you find the right visualization for your data and question. There are many different kinds of plotting graphics available to use (see Chapter 5 of *Fundamentals of Data Visualization* {cite:p}`wilkeviz` for a directory). -The types of plot that we introduce in this book are shown in {numref}`plot_sketches` +The types of plot that we introduce in this book are shown in {numref}`plot_sketches`; which one you should select depends on your data and the question you want to answer. In general, the guiding principles of when to use each type of plot @@ -110,7 +110,7 @@ alternative. +++ ## Refining the visualization -#### *Convey the message, minimize noise* {-} +#### *Convey the message, minimize noise* Just being able to make a visualization in Python with `altair` (or any other tool for that matter) doesn't mean that it effectively communicates your message to @@ -132,7 +132,7 @@ understand and remember your message quickly. - Make sure to use color schemes that are understandable by those with colorblindness (a surprisingly large fraction of the overall population—from about 1% to 10%, depending on sex and ancestry {cite:p}`deebblind`). - For example, [Color Schemes](https://vega.github.io/vega/docs/schemes/) + For example, [Color Schemes](https://altair-viz.github.io/user_guide/customization.html#customizing-colors) provides the ability to pick such color schemes, and you can check your visualizations after you have created them by uploading to online tools such as a [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/). @@ -211,7 +211,8 @@ We see that there are two columns in the `co2_df` data frame; `date_measured` an The `date_measured` column holds the date the measurement was taken, and is of type `datetime64`. The `ppm` column holds the value of CO$_{\text{2}}$ in parts per million -that was measured on each date, and is type `float64`. +that was measured on each date, and is type `float64`; this is the usual +type for decimal numbers. > **Note:** `read_csv` was able to parse the `date_measured` column into the > `datetime` vector type because it was entered @@ -225,7 +226,7 @@ that was measured on each date, and is type `float64`. > (e.g., in the `date_measured` column in the `co2_df` data frame). > This means Python will not accidentally plot the dates in the wrong order > (i.e., not alphanumerically as would happen if it was a character vector). -> More about dates and times can be viewed [here](https://wesmckinney.com/book/time-series.html) +> More about dates and times can be viewed [here](https://wesmckinney.com/book/time-series.html). Since we are investigating a relationship between two variables (CO$_{\text{2}}$ concentration and date), @@ -247,8 +248,9 @@ There are a few basic aspects of a plot that we need to specify: - Here, we use the `mark_point` function to visualize our data as a scatter plot. - The **geometric encoding**, which tells `altair` how the columns in the data frame map to properties of the visualization. - To create an encoding, we use the `encode()` function. - - The `encode()` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name(column names) + - The `encode()` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name (column names) - Here, we set the `x` axis of the plot to the `date_measured` variable, and on the `y` axis, we plot the `ppm` variable. + - For the y-axis, we also provided the argument `scale=alt.Scale(zero=False)`. By default, `altair` chooses the y-limits based on the data and will keep `y=0` in view. That would make it difficult to see any trends in our data since the smallest value is >300 ppm. So by providing `scale=alt.Scale(zero=False)`, we tell altair to choose a reasonable lower bound based on our data, and that lower bound doesn't have to be zero. ```{code-cell} ipython3 :tags: ["remove-cell"] @@ -307,22 +309,12 @@ co2_line = alt.Chart(co2_df).mark_line().encode( ) ``` -For the y-axis, we also provided the argument `scale=alt.Scale(zero=False)`. -By default, `altair` chooses the y-limits based on the data and will keep `y=0` -in view. That would make it difficult to see any trends in our data since the -smallest value is >300 ppm. So by providing `scale=alt.Scale(zero=False)`, we -tell altair to choose a reasonable lower bound based on our data, and that -lower bound doesn't have to be zero. - ```{code-cell} ipython3 :tags: ["remove-cell"] glue('co2_line', co2_line, display=False) ``` -> **Note:** We can change the size of the point and color of the plot by specifying -> `mark_point(size=10, color="black")` - :::{glue:figure} co2_line :figwidth: 700px :name: co2_line @@ -375,7 +367,7 @@ glue('co2_line_labels', co2_line_labels, display=False) Line plot of atmospheric concentration of CO$_{2}$ over time with clearer axes and labels. ::: -> **Note:** The `configure_` function in `altair` supports many other functionalities, which can be viewed [here](https://altair-viz.github.io/user_guide/configuration.html) +> **Note:** The `configure_*` function in `altair` supports many other functionalities for customizing visualizations, for example updating the size of the plot, changing the font color, or many other options that can be viewed [here](https://altair-viz.github.io/user_guide/configuration.html). ```{index} altair; alt.Scale ``` @@ -391,10 +383,11 @@ In particular, here, we will use the `alt.Scale()` function to zoom in on just five years of data (say, 1990-1994). `domain` argument takes a list of length two to specify the upper and lower bounds to limit the axis. +We will use `axis=alt.Axis(tickCount=4)` to add the lines corresponding to each +year in the background to create the final visualization. This helps us to +better visualise the change with each year. ```{code-cell} ipython3 - - co2_line_scale = alt.Chart(co2_df).mark_line(clip=True).encode( x=alt.X( "date_measured", @@ -428,8 +421,6 @@ Interesting! It seems that each year, the atmospheric CO$_{\text{2}}$ increases and finally increases again until the end of the year. In Hawaii, there are two seasons: summer from May through October, and winter from November through April. Therefore, the oscillating pattern in CO$_{\text{2}}$ matches up fairly closely with the two seasons. -As you might have noticed from the code used to create the final visualization -of the `co2_df` data frame, we used `axis=alt.Axis(tickCount=4)` to add the lines corresponding to each year in the background. This helps us to better visualise the change with each year. A useful analogy to constructing a data visualization is painting a picture. @@ -448,6 +439,8 @@ And finally, we work on adding details and refinements to the painting. In our data visualization this would be when we fine tune axis labels, change the font, adjust the point size, and do other related things. + + ### Scatter plots: the Old Faithful eruption time data set ```{index} Old Faithful @@ -481,7 +474,7 @@ visualization. Let's create a scatter plot using the `altair` package with the `waiting` variable on the horizontal axis, the `eruptions` variable on the vertical axis, and the `mark_point` geometric object. By default, `altair` draws only the outline of each point. If we would -like to fill them in, we pass the argument `filled=True` to `mark_point`. +like to fill them in, we pass the argument `filled=True` to `mark_point`. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. The result is shown in {numref}`faithful_scatter`. @@ -509,11 +502,9 @@ We can see in {numref}`faithful_scatter` that the data tend to fall into two groups: one with short waiting and eruption times, and one with long waiting and eruption times. Note that in this case, there is no overplotting: the points are generally nicely visually separated, and the pattern they form -is clear. Also, note that to make the points solid, we used `filled=True` as argument of the `mark_point` function. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. +is clear. In order to refine the visualization, we need only to add axis -labels and make the font more readable: - - +labels and make the font more readable. ```{code-cell} ipython3 faithful_scatter_labels = alt.Chart(faithful).mark_circle().encode( @@ -535,6 +526,16 @@ glue('faithful_scatter_labels', faithful_scatter_labels, display=False) Scatter plot of waiting time and eruption time with clearer axes and labels. ::: + +We can change the size of the point and color of the plot by specifying `mark_circle(size=10, color="black")`. + +```{code-cell} ipython3 +faithful_scatter_labels = alt.Chart(size=10, color="black").mark_circle().encode( + x=alt.X("waiting", title="Waiting Time (mins)"), + y=alt.Y("eruptions", title="Eruption Duration (mins)") +) +``` + +++ ### Axis transformation and colored scatter plots: the Canadian languages data set @@ -949,7 +950,7 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton In {numref}`can_lang_plot_legend`, the points are colored with the default `altair` color palette. This is an appropriate choice for most situations. If you want to use different -colors? In Altair, there are many themes available, which can be viewed [in the documentation](https://vega.github.io/vega/docs/schemes/) +colors? In Altair, there are many themes available, which can be viewed [in the documentation](https://altair-viz.github.io/user_guide/customization.html#customizing-colors) To change the color scheme, we add the `scheme` argument in the `scale` of the `color` argument in `altair` layer indicating the palette we want to use. @@ -1773,7 +1774,7 @@ result. (4) It would be worth further investigating the differences between these experiments to see why they produced different results. ## Saving the visualization -#### *Choose the right output format for your needs* {-} +#### *Choose the right output format for your needs* ```{index} see: bitmap; raster graphics ``` From 048693427748a3a3346ad7e90a09471388753618 Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Thu, 29 Dec 2022 10:06:26 -0700 Subject: [PATCH 05/16] more progress on the viz chapter (part way through morley data) --- source/viz.md | 80 ++++++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 46 deletions(-) diff --git a/source/viz.md b/source/viz.md index 80b6ba80..6a1bc753 100644 --- a/source/viz.md +++ b/source/viz.md @@ -543,7 +543,7 @@ faithful_scatter_labels = alt.Chart(size=10, color="black").mark_circle().encode ```{index} Canadian languages ``` -Recall the `can_lang` data set {cite:p}`timbers2020canlang` from Chapters {ref}`intro`, {ref}`reading`, and {ref}`wrangling`, +Recall the `can_lang` data set {cite:p}`timbers2020canlang` from the {ref}`intro`, {ref}`reading`, and {ref}`wrangling` chapters, which contains counts of languages from the 2016 Canadian census. @@ -638,11 +638,11 @@ Scatter plot of number of Canadians reporting a language as their mother tongue ```{code-cell} ipython3 :tags: ["remove-cell"] import numpy as np -numlang_speakers_max=max(can_lang['mother_tongue']) +numlang_speakers_max=int(max(can_lang['mother_tongue'])) print(numlang_speakers_max) -numlang_speakers_min = min(can_lang['mother_tongue']) +numlang_speakers_min = int(min(can_lang['mother_tongue'])) print(numlang_speakers_min) -log_result = np.floor(np.log(numlang_speakers_max/numlang_speakers_min)) +log_result = int(np.floor(np.log10(numlang_speakers_max/numlang_speakers_min))) print(log_result) glue("numlang_speakers_max", numlang_speakers_max) glue("numlang_speakers_min", numlang_speakers_min) @@ -897,9 +897,11 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton ::: -The legend in {numref}`can_lang_plot_category` -adds a fair bit of horizontal space. -We can improve this by moving the legend title using the `alt.Legend` function + +Another thing we can adjust is the location of the legend. +This is a matter of preference and not critical for the visualization. +We move the legend title using the `alt.Legend` function with the arguments `legendX`, `legendY` and `direction` arguments of the `theme` function. Here we set the `direction` to `"vertical"` so that the legend items remain @@ -949,28 +951,23 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton ::: In {numref}`can_lang_plot_legend`, the points are colored with -the default `altair` color palette. This is an appropriate choice for most situations. If you want to use different -colors? In Altair, there are many themes available, which can be viewed [in the documentation](https://altair-viz.github.io/user_guide/customization.html#customizing-colors) - -To change the color scheme, +the default `altair` color palette. This is an appropriate choice for most situations. In Altair, there are many themes available, which can be viewed [in the documentation](https://altair-viz.github.io/user_guide/customization.html#customizing-colors). To change the color scheme, we add the `scheme` argument in the `scale` of the `color` argument in `altair` layer indicating the palette we want to use. ```{index} color palette; color blindness simulator ``` -You can use -this [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/) to check -if your visualizations -are color-blind friendly. - Below we pick the `"dark2"` theme, with the result shown in {numref}`can_lang_plot_theme` We also set the `shape` aesthetic mapping to the `category` variable as well; this makes the scatter point shapes different for each category. This kind of visual redundancy—i.e., conveying the same information with both scatter point color and shape—can further improve the clarity and accessibility of your visualization. - -> Note: We cannot use different shapes with `mark_circle`, it can only be used with `mark_point` +You can use +this [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/) to check +if your visualizations are color-blind friendly. +Note that we are switching back to the use of `mark_point` so that +we can specify the `shape` attribute. This cannot be done with `mark_circle`. ```{code-cell} ipython3 @@ -1070,7 +1067,7 @@ groups of a categorical variable. We specify that we would like to use a bar plot via the `mark_bar` function in `altair`. -The result is shown in {numref}`islands_bar` +The result is shown in {numref}`islands_bar`. ```{code-cell} ipython3 islands_bar = alt.Chart(islands_df).mark_bar().encode( @@ -1098,13 +1095,13 @@ question we asked was only about the largest landmasses; let's make the plot a little bit clearer by keeping only the largest 12 landmasses. We do this using the `sort_values` function followed by the `iloc` property. Then to help us make sure the labels have enough space, we'll use horizontal bars instead of vertical ones. We do this by -swapping the `x` and `y` variables: +swapping the `x` and `y` variables. ```{index} pandas.DataFrame; nlargest ``` ```{code-cell} ipython3 -islands_top12 = islands_df.nlargest(12) +islands_top12 = islands_df.nlargest(12, "size") islands_bar_sorted = alt.Chart(islands_top12).mark_bar().encode( x="size", y="landmass" @@ -1139,12 +1136,8 @@ we will use the `altair` `sort` function in encoding for `y` axis to organize the landmasses by their `size` variable, which is encoded on the x-axis. To sort the landmasses by their size(denoted on `x` axis), we use `sort='x'`. This plots the values on `y` axis in the ascending order of `x` axis values. - We do this here so that the largest bar will be closest to the axis line, -which is more visually appealing. - -> **Note:** If we want to sort the values on `y-axis` in descending order of `x-axis`, -> we need to specify `sort='-x'`. +which is more visually appealing. If instead, we want to sort the values on `y-axis` in descending order of `x-axis`, we need to specify `sort='-x'`. ```{index} altair; sort ``` @@ -1239,7 +1232,7 @@ let's use the default arguments just to see how things look. ```{code-cell} ipython3 morley_hist = alt.Chart(morley_df).mark_bar().encode( x=alt.X("Speed"), - y="count()" + y=alt.Y("count()") ) ``` @@ -1273,15 +1266,13 @@ We would also like to fine tune this vertical line, styling it so that it is dashed and 1 point in thickness. A point is a measurement unit commonly used with fonts, and 1 point is about 0.353 mm. -We do this by setting `strokeDash=[3,3]` and `size = 1`, respectively. - -Similarly, a horizontal line can be plotted using the `y` axis encoding and the dataframe with one value, which would act as the be the y-intercept +We do this by setting `strokeDash=[3,3]` and `size = 1`, respectively.Similarly, a horizontal line can be plotted using the `y` axis encoding and the dataframe with one value, which would act as the be the y-intercept Note that *vertical lines* are used to denote quantities on the *horizontal axis*, while *horizontal lines* are used to denote quantities on the *vertical axis*. - -To add the dashed line on top of the histogram, we will use the `+` operator. This concept is also known as layering in altair.(This is covered in the later sections of the chapter). Here, we add the `mark_rule` chart on the `morley_hist` chart of the form `mark_bar` +To add the dashed line on top of the histogram, we will use the `+` operator. +This concept is also known as layering in altair. Here, we **add** the `mark_rule` chart to the `morley_hist` chart to produce our final plot. ```{code-cell} ipython3 v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( @@ -1324,8 +1315,8 @@ to make the bars slightly translucent. ```{code-cell} ipython3 morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed"), - y=alt.Y('count()'), - color="Expt" + y=alt.Y("count()"), + color=alt.Color("Expt") ) final_plot_colored = morley_hist_colored + v_line @@ -1349,7 +1340,7 @@ Histogram of Michelson's speed of light data colored by experiment. Alright great, {numref}`final_plot_colored` looks...wait a second! We are not able to distinguish between different Experiments in the histogram! What is going on here? Well, if you -recall from Chapter {ref}`wrangling`, the *data type* you use for each variable +recall from the {ref}`wrangling` chapter, the *data type* you use for each variable can influence how Python and `altair` treats it. Here, we indeed have an issue with the data types in the `morley` data frame. In particular, the `Expt` column is currently an *integer*. But we want to treat it as a @@ -1358,8 +1349,8 @@ is currently an *integer*. But we want to treat it as a ```{index} nominal, altair; :N ``` -To fix this issue we can convert the `Expt` variable into a `nominal`(categorical) type -variable by adding a suffix `:N`(where `N` stands for nominal type variable) with the `Expt` variable. +To fix this issue we can convert the `Expt` variable into a `nominal` (categorical) type +variable by adding a suffix `:N` (where `N` stands for nominal type variable) with the `Expt` variable. By doing this, we are ensuring that `altair` will treat this variable as a categorical variable, and the color will be mapped discretely. Here, we also mention `stack=False`, so that the bars are not stacked on top of each other. @@ -1367,11 +1358,10 @@ and the color will be mapped discretely. Here, we also mention `stack=False`, so morley_hist_categorical = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed", bin=alt.Bin(maxbins=50)), y=alt.Y("count()", stack=False), - color="Expt:N" + color=alt.Color("Expt:N") ) final_plot_categorical = morley_hist_categorical + v_line - ``` ```{code-cell} ipython3 @@ -1415,7 +1405,7 @@ Both the `rows` and `columns` arguments take the column names on which to split morley_hist = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( x=alt.X("Speed", bin=alt.Bin(maxbins=50)), y=alt.Y("count()", stack=False), - color="Expt:N" + color=alt.Color("Expt:N") ).properties(height=100, width=300) final_plot_facet = (morley_hist + v_line).facet( @@ -1451,7 +1441,7 @@ There are two finishing touches to make this visualization even clearer. First a using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. Second, and perhaps more subtly, even though it is easy to compare the experiments on this plot to one another, it is hard to get a sense of just how accurate all the experiments were overall. For example, how accurate is the value 800 on the plot, relative to the true speed of light? -To answer this question, we'll use the assign function to transform our data into a relative measure of accuracy rather than absolute measurements: +To answer this question, we'll use the `assign` function to transform our data into a relative measure of accuracy rather than absolute measurements: ```{code-cell} ipython3 @@ -1515,7 +1505,6 @@ When you create a histogram in `altair`, by default, it tries to choose a reason Naturally, this is not always the right number to use. You can set the number of bins yourself by using the `maxbins` argument in the `mark_bar` geometric object. - But what number of bins is the right one to use? Unfortunately there is no hard rule for what the right bin number @@ -1524,7 +1513,6 @@ or bin width is the one that *helps you answer the question* you asked. Choosing the correct setting for your problem is something that commonly takes iteration. - It's usually a good idea to try out several `maxbins` to see which one most clearly captures your data in the context of the question you want to answer. @@ -1610,12 +1598,12 @@ morley_hist_5 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( ) ).properties(height=100, width=300) -final_plot_max_bins = ( +final_plot_max_bins = (( (morley_hist_default + v_line).facet(row="Expt:N", data=morley_rel, title="default maxbins") | (morley_hist_200 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=200")) & ((morley_hist_70 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=70") | (morley_hist_5 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=5") -) +)) ``` ```{code-cell} ipython3 @@ -1630,7 +1618,7 @@ glue("final_plot_max_bins", final_plot_max_bins, display=True) Effect of varying number of max bins on histograms. ::: -#### Adding layers to a `altair` plot object {+} +#### Adding layers to a `altair` plot object ```{index} altair; + ``` From 229936b631897ab9a10f226a79bfc54aebf7e18a Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Mon, 2 Jan 2023 14:12:00 -0800 Subject: [PATCH 06/16] add back code to create the csv for mauna_loa --- source/viz.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/source/viz.md b/source/viz.md index 6a1bc753..ff910903 100644 --- a/source/viz.md +++ b/source/viz.md @@ -192,6 +192,15 @@ For this book, we are going to focus on the last 40 years of the data set, **Question:** Does the concentration of atmospheric CO$_{\text{2}}$ change over time, and are there any interesting patterns to note? +```{code-cell} ipython3 +:tags: ["remove-cell"] +mauna_loa = pd.read_csv("data/mauna_loa.csv") +mauna_loa['day']=1 +mauna_loa['date_measured']=pd.to_datetime(mauna_loa[["year", "month", "day"]]) +mauna_loa = mauna_loa[['date_measured', 'ppm']].query('ppm>0 and date_measured>"1980-1-1"') +mauna_loa.to_csv("data/mauna_loa_data.csv", index=False) +``` + To get started, we will read and inspect the data: ```{code-cell} ipython3 From 0322baf941926a433cad0bc27a1e29d8632f3b31 Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Mon, 2 Jan 2023 16:55:53 -0800 Subject: [PATCH 07/16] a couple minor typo fixes --- source/viz.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/viz.md b/source/viz.md index ff910903..951aef21 100644 --- a/source/viz.md +++ b/source/viz.md @@ -746,7 +746,7 @@ glue("result", result) ``` -Similar to some of the examples in Chapter {ref}`wrangling`, +Similar to some of the examples in the chapter on {ref}`wrangling`, we can convert the counts to percentages to give them context and make them easier to understand. We can do this by dividing the number of people reporting a given language @@ -1155,7 +1155,7 @@ To label the x and y axes, we will use the `alt.X` and `alt.Y` function The default label is the name of the column being mapped to `color`. Here that would be `landmass_type`; however `landmass_type` is not proper English (and so is less readable). -Thus we use the `title` argument inside `alt.Color` to change that to "Type" +Thus we use the `title` argument inside `alt.Color` to change that to "Type". Finally, we again use the `configure_axis` function to change the font size. @@ -1216,6 +1216,7 @@ First, we read in the data. ```{code-cell} ipython3 morley_df = pd.read_csv("data/morley.csv") +morley_df ``` ```{index} distribution, altair; histogram @@ -1235,7 +1236,10 @@ by separating the data into bins, and then using vertical bars to show how many data points fell in each bin. To create a histogram in `altair` we will use the `mark_bar` geometric -object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `count()`. As usual, +object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `count()`. +There is no `count()` column-name in `morley_df`; we use `count()` to tell `altair` +that we want to count the number of values in the `Speed` column in each bin. +As usual, let's use the default arguments just to see how things look. ```{code-cell} ipython3 From 9ca58279d4d443e245040357118600afd868faa4 Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Wed, 4 Jan 2023 12:32:55 -0800 Subject: [PATCH 08/16] polishing ch4 --- source/viz.md | 281 ++++++++++++++++++++++++-------------------------- 1 file changed, 137 insertions(+), 144 deletions(-) diff --git a/source/viz.md b/source/viz.md index 951aef21..b810b11b 100644 --- a/source/viz.md +++ b/source/viz.md @@ -258,7 +258,7 @@ There are a few basic aspects of a plot that we need to specify: - The **geometric encoding**, which tells `altair` how the columns in the data frame map to properties of the visualization. - To create an encoding, we use the `encode()` function. - The `encode()` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name (column names) - - Here, we set the `x` axis of the plot to the `date_measured` variable, and on the `y` axis, we plot the `ppm` variable. + - Here, we set the `x` axis of the plot to the `date_measured` variable, and on the `y` axis, we plot the `ppm` variable. We use `alt.X` and `alt.Y` which allow you to control properties of the `x` and `y` axes. - For the y-axis, we also provided the argument `scale=alt.Scale(zero=False)`. By default, `altair` chooses the y-limits based on the data and will keep `y=0` in view. That would make it difficult to see any trends in our data since the smallest value is >300 ppm. So by providing `scale=alt.Scale(zero=False)`, we tell altair to choose a reasonable lower bound based on our data, and that lower bound doesn't have to be zero. ```{code-cell} ipython3 @@ -268,7 +268,7 @@ from myst_nb import glue ```{code-cell} ipython3 co2_scatter = alt.Chart(co2_df).mark_point().encode( - x="date_measured", + x=alt.X("date_measured"), y=alt.Y("ppm", scale=alt.Scale(zero=False)) ) ``` @@ -313,7 +313,7 @@ with just the default arguments: ```{code-cell} ipython3 co2_line = alt.Chart(co2_df).mark_line().encode( - x="date_measured", + x=alt.X("date_measured"), y=alt.Y("ppm", scale=alt.Scale(zero=False)) ) ``` @@ -357,11 +357,9 @@ change the font size, we use the `configure_axis` function with the ```{code-cell} ipython3 co2_line_labels = alt.Chart(co2_df).mark_line().encode( - x=alt.X("date_measured", title ="Year"), + x=alt.X("date_measured", title="Year"), y=alt.Y("ppm", scale=alt.Scale(zero=False), title="Atmospheric CO2 (ppm)") -).configure_axis( - titleFontSize=12 -) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -389,10 +387,13 @@ another important feature of `altair` that easily transforms the different variables and set limits. We scale the horizontal axis using the `alt.Scale(domain=['1990', '1993'])` by restricting the x-axis values between 1990 and 1994, and the vertical axis with the `alt.Scale(zero=False)` function, to not start the y-axis with zero. In particular, here, we will use the `alt.Scale()` function to zoom in -on just five years of data (say, 1990-1994). +on just five years of data (say, 1990-1994). The `domain` argument takes a list of length two to specify the upper and lower bounds to limit the axis. -We will use `axis=alt.Axis(tickCount=4)` to add the lines corresponding to each +We also added the argument `clip=True` to `mark_line`. This tells `altair` +to "clip" the data outside of the domain that we set so that it doesn't +extend past the plot area. +Finally, we will use `axis=alt.Axis(tickCount=4)` to add the lines corresponding to each year in the background to create the final visualization. This helps us to better visualise the change with each year. @@ -409,9 +410,7 @@ co2_line_scale = alt.Chart(co2_df).mark_line(clip=True).encode( scale=alt.Scale(zero=False), title="Atmospheric CO2 (ppm)" ) -).configure_axis( - titleFontSize=12 -) +).configure_axis(titleFontSize=12) ``` ```{code-cell} ipython3 @@ -486,8 +485,6 @@ By default, `altair` draws only the outline of each point. If we would like to fill them in, we pass the argument `filled=True` to `mark_point`. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. The result is shown in {numref}`faithful_scatter`. - - ```{code-cell} ipython3 faithful_scatter = alt.Chart(faithful).mark_point(filled=True).encode( x="waiting", @@ -522,10 +519,9 @@ faithful_scatter_labels = alt.Chart(faithful).mark_circle().encode( ) ``` - ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('faithful_scatter_labels', faithful_scatter_labels, display=False) +glue("faithful_scatter_labels", faithful_scatter_labels, display=False) ``` :::{glue:figure} faithful_scatter_labels @@ -539,12 +535,24 @@ Scatter plot of waiting time and eruption time with clearer axes and labels. We can change the size of the point and color of the plot by specifying `mark_circle(size=10, color="black")`. ```{code-cell} ipython3 -faithful_scatter_labels = alt.Chart(size=10, color="black").mark_circle().encode( +faithful_scatter_labels_black = alt.Chart(size=10, color="black").mark_circle().encode( x=alt.X("waiting", title="Waiting Time (mins)"), y=alt.Y("eruptions", title="Eruption Duration (mins)") ) ``` +```{code-cell} ipython3 +:tags: ["remove-cell"] +glue('faithful_scatter_labels_black', faithful_scatter_labels_black, display=False) +``` + +:::{glue:figure} faithful_scatter_labels_black +:figwidth: 700px +:name: faithful_scatter_labels_black + +Scatter plot of waiting time and eruption time with black points. +::: + +++ ### Axis transformation and colored scatter plots: the Canadian languages data set @@ -552,8 +560,8 @@ faithful_scatter_labels = alt.Chart(size=10, color="black").mark_circle().encode ```{index} Canadian languages ``` -Recall the `can_lang` data set {cite:p}`timbers2020canlang` from the {ref}`intro`, {ref}`reading`, and {ref}`wrangling` chapters, -which contains counts of languages from the 2016 +Recall the `can_lang` data set {cite:p}`timbers2020canlang` from the {ref}`intro`, {ref}`reading`, and {ref}`wrangling` chapters. +It contains counts of languages from the 2016 Canadian census. ```{index} question; visualization @@ -569,7 +577,9 @@ non-official and non-Aboriginal languages)? To get started, we will read and inspect the data: ```{code-cell} ipython3 +:tags: ["output_scroll"] can_lang = pd.read_csv("data/can_lang.csv") +can_lang ``` ```{code-cell} ipython3 @@ -667,7 +677,7 @@ the upper right corner) than other languages. In particular, the most common mother tongue language has {glue:}`numlang_speakers_max` speakers, while the least common has only {glue:}`numlang_speakers_min`. -That's a {glue:}`log_result` -decimal-place difference +That's a six-decimal-place difference in the magnitude of these two numbers! We can confirm that the two points in the upper right-hand corner correspond to Canada's two official languages by filtering the data: @@ -676,8 +686,10 @@ to Canada's two official languages by filtering the data: ``` ```{code-cell} ipython3 +:tags: ["output_scroll"] can_lang.loc[ - (can_lang['language']=='English') | (can_lang['language']=='French') + (can_lang['language']=='English') | + (can_lang['language']=='French') ] ``` @@ -975,6 +987,7 @@ further improve the clarity and accessibility of your visualization. You can use this [color blindness simulator](https://www.color-blindness.com/coblis-color-blindness-simulator/) to check if your visualizations are color-blind friendly. +The default color palattes in `altair` are color-blind friendly (one more reason to stick with the defaults!). Note that we are switching back to the use of `mark_point` so that we can specify the `shape` attribute. This cannot be done with `mark_circle`. @@ -1059,6 +1072,7 @@ The `islands.csv` data set contains a list of Earth's landmasses as well as thei To get started, we will read and inspect the data: ```{code-cell} ipython3 +:tags: ["output_scroll"] islands_df = pd.read_csv("data/islands.csv") islands_df ``` @@ -1090,7 +1104,7 @@ glue('islands_bar', islands_bar, display=False) ``` :::{glue:figure} islands_bar -:figwidth: 700px +:figwidth: 400px :name: islands_bar Bar plot of all Earth's landmasses' size with squished labels. @@ -1099,10 +1113,12 @@ Bar plot of all Earth's landmasses' size with squished labels. Alright, not bad! The plot in {numref}`islands_bar` is definitely the right kind of visualization, as we can clearly see and compare sizes of landmasses. The major issues are that the smaller landmasses' sizes -are hard to distinguish, and the names of the landmasses are tilted by default to fit in the labels. But remember that the +are hard to distinguish, and the plot is so wide that we can't compare them all! But remember that the question we asked was only about the largest landmasses; let's make the plot a little bit clearer by keeping only the largest 12 landmasses. We do this using -the `sort_values` function followed by the `iloc` property. Then to help us make sure the labels have enough +the `nlargest` function; the first argument is the number of rows we want and +the second is the name of the column we want to use for comparing who is +largest. Then to help us make sure the labels have enough space, we'll use horizontal bars instead of vertical ones. We do this by swapping the `x` and `y` variables. @@ -1155,14 +1171,14 @@ To label the x and y axes, we will use the `alt.X` and `alt.Y` function The default label is the name of the column being mapped to `color`. Here that would be `landmass_type`; however `landmass_type` is not proper English (and so is less readable). -Thus we use the `title` argument inside `alt.Color` to change that to "Type". +Thus we use the `title` argument inside `alt.Color` to change that to `"Type"`. Finally, we again use the `configure_axis` function to change the font size. ```{code-cell} ipython3 islands_plot_sorted = alt.Chart(islands_top12).mark_bar().encode( x=alt.X("size",title="Size (1000 square mi)"), - y=alt.Y("landmass", title="Landmass", sort='x'), + y=alt.Y("landmass", title="Landmass", sort="x"), color=alt.Color("landmass_type", title="Type") ).configure_axis(titleFontSize=12) ``` @@ -1185,6 +1201,32 @@ visualization for answering our original questions. Landmasses are organized by their size, and continents are colored differently than other landmasses, making it quite clear that continents are the largest seven landmasses. +There is one more finishing touch we will make... We can also add a title to the chart by specifying `title` argument in the `alt.Chart` function + +```{code-cell} ipython3 +islands_plot_sorted = alt.Chart(islands_top12, title="Largest 12 landmasses on Earth").mark_bar().encode( + x=alt.X("size",title="Size (1000 square mi)"), + y=alt.Y("landmass", title="Landmass", sort="x"), + color=alt.Color("landmass_type", title="Type") +).configure_axis(titleFontSize=12) +``` + +```{code-cell} ipython3 +:tags: ["remove-cell"] +glue('islands_plot_sorted', islands_plot_sorted, display=True) +``` + +:::{glue:figure} islands_plot_sorted +:figwidth: 700px +:name: islands_plot_sorted + +Bar plot of size for Earth's largest 12 landmasses now with a title. +::: + +> **Note:** Good visualization titles clearly communicate +> the take home message to the audience. Typically, +> that is the answer to the question you posed before making the visualization. + ### Histograms: the Michelson speed of light data set ```{index} Michelson speed of light @@ -1261,36 +1303,49 @@ glue("morley_hist", morley_hist, display=False) Histogram of Michelson's speed of light data. ::: -```{index} altair; mark_rule +#### Adding layers to an `altair` plot object + +```{index} altair; +; mark_rule ``` {numref}`morley_hist` is a great start. However, we cannot tell how accurate the measurements are using this visualization unless we can see the true value. + +One of the powerful features of `altair` is that you +can continue to iterate on a single plot object, adding and refining +one layer at a time. If you stored your plot as a named object +using the assignment symbol (`=`), you can +add to it using the `+` operator. +For example, if we wanted to add a vertical line to the last plot we created (`morley_hist`), +we can use the `+` operator to add a vertical line chart layer with the `mark_rule` function. + In order to visualize the true speed of light, we will add a vertical line with the `mark_rule` function. To draw a vertical line with `mark_rule`, we need to specify where on the x-axis the line should be drawn. -We can do this by creating a dataframe with just one column with value `792.458`, which is the true value of light speed -minus 299,000 and encoding it in the `x` axis; this ensures it is coded the same way as the -measurements in the `morley` data frame. +We can do this by providing `x=alt.datum(792.458)`. The value `792.458` +is the true value of light speed +minus 299,000. Using `alt.datum` tells altair that we have a single datum +(number) that we would like plotted. We would also like to fine tune this vertical line, -styling it so that it is dashed and 1 point in thickness. -A point is a measurement unit commonly used with fonts, -and 1 point is about 0.353 mm. -We do this by setting `strokeDash=[3,3]` and `size = 1`, respectively.Similarly, a horizontal line can be plotted using the `y` axis encoding and the dataframe with one value, which would act as the be the y-intercept +styling it so that it is dashed, +we do this by setting `strokeDash=[3]`. Note that you could also +change the thickness of the line by providing `size=2` if you wanted to. +Similarly, a horizontal line can be plotted using the `y` axis encoding and +the dataframe with one value, which would act as the be the y-intercept. Note that *vertical lines* are used to denote quantities on the *horizontal axis*, while *horizontal lines* are used to denote quantities on the *vertical axis*. -To add the dashed line on top of the histogram, we will use the `+` operator. +To add the dashed line on top of the histogram, we use the `+` operator. This concept is also known as layering in altair. Here, we **add** the `mark_rule` chart to the `morley_hist` chart to produce our final plot. ```{code-cell} ipython3 -v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( - strokeDash=[3,3], size=1 -).encode(x="x") +v_line = alt.Chart().mark_rule(strokeDash=[3]).encode( + x=alt.datum(792.458) +) final_plot = morley_hist + v_line ``` @@ -1298,17 +1353,17 @@ final_plot = morley_hist + v_line ```{code-cell} ipython3 :tags: ["remove-cell"] -glue("final_plot_viz", final_plot, display=False) +glue("final_plot", final_plot, display=False) ``` -:::{glue:figure} final_plot_viz +:::{glue:figure} final_plot :figwidth: 700px -:name: final_plot_viz +:name: final_plot Histogram of Michelson's speed of light data with vertical line indicating true speed of light. ::: -In {numref}`final_plot_viz`, +In {numref}`final_plot`, we still cannot tell which experiments (denoted in the `Expt` column) led to which measurements; perhaps some experiments were more accurate than others. @@ -1351,7 +1406,7 @@ Histogram of Michelson's speed of light data colored by experiment. ```{index} integer ``` -Alright great, {numref}`final_plot_colored` looks...wait a second! We are not able to distinguish +Alright great, {numref}`final_plot_colored` looks... wait a second! We are not able to distinguish between different Experiments in the histogram! What is going on here? Well, if you recall from the {ref}`wrangling` chapter, the *data type* you use for each variable can influence how Python and `altair` treats it. Here, we indeed have an issue @@ -1389,8 +1444,6 @@ glue('final_plot_categorical', final_plot_categorical, display=True) Histogram of Michelson's speed of light data colored by experiment as a categorical variable. ::: - - Unfortunately, the attempt to separate out the experiment number visually has created a bit of a mess. All of the colors in {numref}`final_plot_categorical` are blending together, and although it is possible to derive *some* insight from this (e.g., experiments 1 and 3 had some @@ -1406,23 +1459,36 @@ grid of separate histogram plots. We use the `facet` function to create a plot that has multiple subplots arranged in a grid. The argument to `facet` specifies the variable(s) used to split the plot -into subplots, and how to split them (i.e., into rows or columns). -If the plot is to be split horizontally, into rows, +into subplots (`Expt`), the data frame we are working with `morley_df`, and +how to split them (i.e., into rows or columns). In this example, we choose to +have our plots in a single column (`columns=1`). This makes it easier for +us to compare along the `x`-axis as our vertical-line is in the same +horizontal position. If instead you wanted to use a single row, you could +specify `rows=1`. + + + + +There is another important change we have made. You will notice that when +we define `morley_hist`, we are no longer supplying `morley_df` as an +argument to `alt.Chart`. This is because `facet` takes care of separating +the data by `Expt` and providing it to each of the facet sub-plots. ```{code-cell} ipython3 -morley_hist = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( +morley_hist = alt.Chart().mark_bar(opacity=0.5).encode( x=alt.X("Speed", bin=alt.Bin(maxbins=50)), y=alt.Y("count()", stack=False), color=alt.Color("Expt:N") -).properties(height=100, width=300) +).properties(height=100, width=400) final_plot_facet = (morley_hist + v_line).facet( - row="Expt:N", data=morley_df + "Expt", + data=morley_df, + columns=1 ) ``` @@ -1450,8 +1516,8 @@ The most different experiments still obtained quite similar results! ```{index} altair; alt.X, altair; alt.Y, altair; configure_axis ``` -There are two finishing touches to make this visualization even clearer. First and foremost, we need to add informative axis labels -using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. Second, and perhaps more subtly, even though it +There are three finishing touches to make this visualization even clearer. First and foremost, we need to add informative axis labels +using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. We should add a title! For a `facet` plot, this is done by providing the `title` to the facet function. Finally, and perhaps most subtly, even though it is easy to compare the experiments on this plot to one another, it is hard to get a sense of just how accurate all the experiments were overall. For example, how accurate is the value 800 on the plot, relative to the true speed of light? To answer this question, we'll use the `assign` function to transform our data into a relative measure of accuracy rather than absolute measurements: @@ -1469,9 +1535,9 @@ morley_rel ``` ```{code-cell} ipython3 -v_line = alt.Chart(pd.DataFrame({"x": [0]})).mark_rule( - strokeDash=[3,3], size=2).encode( - x="x" +v_line = alt.Chart().mark_rule( + strokeDash=[3]).encode( + x=alt.datum(0) ) morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( @@ -1489,10 +1555,13 @@ morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( "Expt:N", title="Experiment ID" ) -).properties(height=100, width= 400) +).properties(height=100, width=400) final_plot_relative = (morley_hist + v_line).facet( - row="Expt:N", data=morley_rel + "Expt", + data=morley_rel, + columns=1, + title="Histogram of relative accuracy of Michelson’s speed of light data" ) ``` @@ -1545,7 +1614,7 @@ On the other hand, the `maxbins=200` and `maxbins=5` are too small and too big, ```{code-cell} ipython3 :tags: ["remove-cell"] -morley_hist_default = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( +morley_hist_default = alt.Chart().mark_bar(opacity=0.9).encode( x=alt.X( "relative_accuracy", title="Relative Accuracy (%)" @@ -1559,9 +1628,9 @@ morley_hist_default = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( "Expt:N", title="Experiment ID" ) -).properties(height=100, width=400) +).properties(height=100, width=200) -morley_hist_200 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( +morley_hist_200 = alt.Chart().mark_bar(opacity=0.9).encode( x=alt.X( "relative_accuracy", bin=alt.Bin(maxbins=200), @@ -1575,9 +1644,9 @@ morley_hist_200 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( color=alt.Color( "Expt:N", title="Experiment ID" ) -).properties(height=100, width=400) +).properties(height=100, width=200) -morley_hist_70 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( +morley_hist_70 = alt.Chart().mark_bar(opacity=0.9).encode( x=alt.X( "relative_accuracy", bin=alt.Bin(maxbins=70), @@ -1592,9 +1661,9 @@ morley_hist_70 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( "Expt:N", title="Experiment ID" ) -).properties(height=100, width=400) +).properties(height=100, width=200) -morley_hist_5 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( +morley_hist_5 = alt.Chart().mark_bar(opacity=0.9).encode( x=alt.X( "relative_accuracy", bin=alt.Bin(maxbins=5), @@ -1609,7 +1678,7 @@ morley_hist_5 = alt.Chart(morley_rel).mark_bar(opacity=0.6).encode( "Expt:N", title="Experiment ID" ) -).properties(height=100, width=300) +).properties(height=100, width=200) final_plot_max_bins = (( (morley_hist_default + v_line).facet(row="Expt:N", data=morley_rel, title="default maxbins") | @@ -1631,76 +1700,6 @@ glue("final_plot_max_bins", final_plot_max_bins, display=True) Effect of varying number of max bins on histograms. ::: -#### Adding layers to a `altair` plot object - -```{index} altair; + -``` - -One of the powerful features of `altair` is that you -can continue to iterate on a single plot object, adding and refining -one layer at a time. If you stored your plot as a named object -using the assignment symbol (`=`), you can -add to it using the `+` operator. -For example, if we wanted to add a vertical line to the last plot we created (`morley_hist`), -we can use the `+` operator to add a vertical line chart layer with the `mark_rule` function. -The result is shown in {numref}`morley_hist_layer`. - -```{code-cell} ipython3 -morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( - x=alt.X("Speed"), - y=alt.Y("count()"), - color="Expt:N" -) - -v_line = alt.Chart(pd.DataFrame({"x": [792.458]})).mark_rule( - strokeDash=[3,3], size=1 -).encode(x="x") - -morley_hist_layer = morley_hist_colored + v_line -``` - -```{code-cell} ipython3 -:tags: ["remove-cell"] -glue("morley_hist_layer", morley_hist_layer, display=True) -``` - -:::{glue:figure} morley_hist_layer -:figwidth: 700px -:name: morley_hist_layer - -Histogram of Michelson's speed of light data colored by experiment with layered vertical line. -::: - - -We can also add a title to the chart by specifying `title` argument in the `alt.Chart` function - - -```{code-cell} ipython3 -morley_hist_title=alt.Chart(morley_df, title="Histogram of Michelson's speed of light data colored by experiment").mark_bar(opacity=0.5).encode( - x=alt.X("Speed"), - y=alt.Y("count()"), - color="Expt:N" -) - - -``` -```{code-cell} ipython3 -:tags: ["remove-cell"] -glue('morley_hist_title', morley_hist_title, display=True) -``` - -:::{glue:figure} morley_hist_title -:figwidth: 700px -:name: morley_hist_title - -Histogram of Michelson's speed of light data colored with title -::: - - -> **Note:** Good visualization titles clearly communicate -> the take home message to the audience. Typically, -> that is the answer to the question you posed before making the visualization. - ## Explaining the visualization #### *Tell a story* @@ -1857,14 +1856,8 @@ to create a PNG image file, we specify that the file extension is `.png`. Below we demonstrate how to save PNG and SVG file types for the `faithful_scater_labels` plot: -```{code-cell} ipython3 -:tags: ["remove-cell"] -!pip install altair_saver -``` - ```{code-cell} ipython3 -#!pip install altair_saver #uncomment and run in jupyter notebook to install altair_saver, if not already installed from altair_saver import save faithful_scatter_labels.save("faithful_plot.png") @@ -1930,7 +1923,7 @@ Zoomed in `faithful`, raster (PNG, left) and vector (SVG, right) formats. Practice exercises for the material covered in this chapter can be found in the accompanying -[worksheets repository](https://github.com/UBC-DSCI/data-science-a-first-intro-worksheets#readme) +[worksheets repository](https://github.com/UBC-DSCI/data-science-a-first-intro-python-worksheets#readme) in the "Effective data visualization" row. You can launch an interactive version of the worksheet in your browser by clicking the "launch binder" button. You can also preview a non-interactive version of the worksheet by clicking "view worksheet." From e099f3abf4ae8e718874a8c3427354f994f8dd52 Mon Sep 17 00:00:00 2001 From: Lindsey Heagy Date: Wed, 4 Jan 2023 12:38:02 -0800 Subject: [PATCH 09/16] minor polish on ch4 --- source/viz.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/viz.md b/source/viz.md index b810b11b..6ba7d241 100644 --- a/source/viz.md +++ b/source/viz.md @@ -918,8 +918,6 @@ Scatter plot of percentage of Canadians reporting a language as their mother ton ::: - Another thing we can adjust is the location of the legend. This is a matter of preference and not critical for the visualization. We move the legend title using the `alt.Legend` function From 3fbc74e4a1754c88fdb320cb829c245045ea23f0 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 14:19:17 -0800 Subject: [PATCH 10/16] code tags in learning objs --- source/viz.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/viz.md b/source/viz.md index 6ba7d241..ef2fff28 100644 --- a/source/viz.md +++ b/source/viz.md @@ -40,9 +40,9 @@ By the end of the chapter, readers will be able to do the following: - mark objects - encodings - Use the altair library in Python to create and refine the above visualizations using: - - mark objects: mark_point, mark_line, mark_bar - - encodings : x, y, fill, color, shape - - subplots: facet + - mark objects: `mark_point`, `mark_line`, `mark_bar` + - encodings : `x`, `y`, `fill`, `color`, `shape` + - subplots: `facet` - Describe the difference in raster and vector output formats. - Use `chart.save()` to save visualizations in `.png` and `.svg` format. From 784f62318c68438ac3cd117e09029177d140a65a Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 14:31:14 -0800 Subject: [PATCH 11/16] polish on ch4, fixed number -> percentage in figure labels --- source/viz.md | 56 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 21 deletions(-) diff --git a/source/viz.md b/source/viz.md index ef2fff28..0c87bc5d 100644 --- a/source/viz.md +++ b/source/viz.md @@ -63,7 +63,7 @@ if you aren't standing at the poster explaining things, an effective visualization will convey your message to the audience. Recall the different data analysis questions -from Chapter \@ref(intro). +from the {ref}`intro` chapter. With the visualizations we will cover in this chapter, we will be able to answer *only descriptive and exploratory* questions. Be careful to not answer any *predictive, inferential, causal* @@ -251,15 +251,25 @@ There are a few basic aspects of a plot that we need to specify: ``` - The name of the **data frame** object to visualize. - - Here, we specify the `co2_df` data frame as an argument to the `alt.Chart()` function + - Here, we specify the `co2_df` data frame as an argument to `alt.Chart` - The **geometric object**, which specifies how the mapped data should be displayed. - - To create a geometric object, we use `Chart.mark_*` methods (see the [altair reference](https://altair-viz.github.io/user_guide/marks.html) for a list of geometric objects). + - To create a geometric object, we use `Chart.mark_*` methods (see the + [altair reference](https://altair-viz.github.io/user_guide/marks.html) + for a list of geometric objects). - Here, we use the `mark_point` function to visualize our data as a scatter plot. - The **geometric encoding**, which tells `altair` how the columns in the data frame map to properties of the visualization. - - To create an encoding, we use the `encode()` function. - - The `encode()` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name (column names) - - Here, we set the `x` axis of the plot to the `date_measured` variable, and on the `y` axis, we plot the `ppm` variable. We use `alt.X` and `alt.Y` which allow you to control properties of the `x` and `y` axes. - - For the y-axis, we also provided the argument `scale=alt.Scale(zero=False)`. By default, `altair` chooses the y-limits based on the data and will keep `y=0` in view. That would make it difficult to see any trends in our data since the smallest value is >300 ppm. So by providing `scale=alt.Scale(zero=False)`, we tell altair to choose a reasonable lower bound based on our data, and that lower bound doesn't have to be zero. + - To create an encoding, we use the `encode` function. + - The `encode` method builds a key-value mapping between encoding channels (such as x, y) to fields in the dataset, accessed by field name (column names) + - Here, we set the `x` axis of the plot to the `date_measured` variable, + and on the `y` axis, we plot the `ppm` variable. We use `alt.X` and + `alt.Y` which allow you to control properties of the `x` and `y` axes. + - For the y-axis, we also provided the argument + `scale=alt.Scale(zero=False)`. By default, `altair` chooses the y-limits + based on the data and will keep `y=0` in view. That would make it + difficult to see any trends in our data since the smallest value is >300 + ppm. So by providing `scale=alt.Scale(zero=False)`, we tell altair to + choose a reasonable lower bound based on our data, and that lower bound + doesn't have to be zero. ```{code-cell} ipython3 :tags: ["remove-cell"] @@ -285,7 +295,7 @@ glue('co2_scatter', co2_scatter, display=False) Scatter plot of atmospheric concentration of CO$_{2}$ over time. ::: -Certainly, the visualization in {numref}`co2_scatter` +The visualization in {numref}`co2_scatter` shows a clear upward trend in the atmospheric concentration of CO$_{\text{2}}$ over time. This plot answers the first part of our question in the affirmative, @@ -386,7 +396,7 @@ answer. We will accomplish this by using *scale*, another important feature of `altair` that easily transforms the different variables and set limits. We scale the horizontal axis using the `alt.Scale(domain=['1990', '1993'])` by restricting the x-axis values between 1990 and 1994, and the vertical axis with the `alt.Scale(zero=False)` function, to not start the y-axis with zero. -In particular, here, we will use the `alt.Scale()` function to zoom in +In particular, here, we will use the `alt.Scale` function to zoom in on just five years of data (say, 1990-1994). The `domain` argument takes a list of length two to specify the upper and lower bounds to limit the axis. @@ -425,9 +435,12 @@ glue('co2_line_scale', co2_line_scale, display=False) Line plot of atmospheric concentration of CO$_{2}$ from 1990 to 1994. ::: -Interesting! It seems that each year, the atmospheric CO$_{\text{2}}$ increases until it reaches its peak somewhere around April, decreases until around late September, -and finally increases again until the end of the year. In Hawaii, there are two seasons: summer from May through October, and winter from November through April. -Therefore, the oscillating pattern in CO$_{\text{2}}$ matches up fairly closely with the two seasons. +Interesting! It seems that each year, the atmospheric CO$_{\text{2}}$ increases +until it reaches its peak somewhere around April, decreases until around late +September, and finally increases again until the end of the year. In Hawaii, +there are two seasons: summer from May through October, and winter from +November through April. Therefore, the oscillating pattern in CO$_{\text{2}}$ +matches up fairly closely with the two seasons. @@ -482,7 +495,8 @@ visualization. Let's create a scatter plot using the `altair` package with the `waiting` variable on the horizontal axis, the `eruptions` variable on the vertical axis, and the `mark_point` geometric object. By default, `altair` draws only the outline of each point. If we would -like to fill them in, we pass the argument `filled=True` to `mark_point`. In place of `mark_point(filled=True)`, we can also use `mark_circle()`. +like to fill them in, we pass the argument `filled=True` to `mark_point`. In +place of `mark_point(filled=True)`, we can also use `mark_circle`. The result is shown in {numref}`faithful_scatter`. ```{code-cell} ipython3 @@ -535,7 +549,7 @@ Scatter plot of waiting time and eruption time with clearer axes and labels. We can change the size of the point and color of the plot by specifying `mark_circle(size=10, color="black")`. ```{code-cell} ipython3 -faithful_scatter_labels_black = alt.Chart(size=10, color="black").mark_circle().encode( +faithful_scatter_labels_black = alt.Chart(faithful).mark_circle(size=10, color="black").encode( x=alt.X("waiting", title="Waiting Time (mins)"), y=alt.Y("eruptions", title="Eruption Duration (mins)") ) @@ -800,13 +814,13 @@ the final result. can_lang_plot_percent = alt.Chart(can_lang).mark_circle().encode( x=alt.X( "most_at_home_percent", - title=["Language spoken most at home", "(number of Canadian residents)"], + title=["Language spoken most at home", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), y=alt.Y( "mother_tongue_percent", - title=["Mother tongue", "(number of Canadian residents)"], + title=["Mother tongue", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ) @@ -890,13 +904,13 @@ plot. can_lang_plot_category=alt.Chart(can_lang).mark_circle().encode( x=alt.X( "most_at_home_percent", - title=["Language spoken most at home", "(number of Canadian residents)"], + title=["Language spoken most at home", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), y=alt.Y( "mother_tongue_percent", - title=["Mother tongue", "(number of Canadian residents)"], + title=["Mother tongue", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), @@ -935,13 +949,13 @@ legend above the plot instead. can_lang_plot_legend = alt.Chart(can_lang).mark_circle().encode( x=alt.X( "most_at_home_percent", - title=["Language spoken most at home", "(number of Canadian residents)"], + title=["Language spoken most at home", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), y=alt.Y( "mother_tongue_percent", - title=["Mother tongue", "(number of Canadian residents)"], + title=["Mother tongue", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), @@ -994,7 +1008,7 @@ we can specify the `shape` attribute. This cannot be done with `mark_circle`. can_lang_plot_theme = alt.Chart(can_lang).mark_point(filled=True).encode( x=alt.X( "most_at_home_percent", - title=["Language spoken most at home", "(number of Canadian residents)"], + title=["Language spoken most at home", "(percentage of Canadian residents)"], scale=alt.Scale(type="log"), axis=alt.Axis(tickCount=7) ), From 0d86299c6009a61991d03ec374c6fe938c717557 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 15:03:32 -0800 Subject: [PATCH 12/16] re-added other filetypes... --- source/viz.md | 180 ++++++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 87 deletions(-) diff --git a/source/viz.md b/source/viz.md index 0c87bc5d..6845ce2e 100644 --- a/source/viz.md +++ b/source/viz.md @@ -1140,25 +1140,25 @@ swapping the `x` and `y` variables. ```{code-cell} ipython3 islands_top12 = islands_df.nlargest(12, "size") -islands_bar_sorted = alt.Chart(islands_top12).mark_bar().encode( +islands_bar_top = alt.Chart(islands_top12).mark_bar().encode( x="size", y="landmass" ) ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('islands_bar_sorted', islands_bar_sorted, display=True) +glue('islands_bar_top', islands_bar_top, display=True) ``` -:::{glue:figure} islands_bar_sorted +:::{glue:figure} islands_bar_top :figwidth: 700px -:name: islands_bar_sorted +:name: islands_bar_top Bar plot of size for Earth's largest 12 landmasses. ::: -The plot in {numref}`islands_bar_sorted` is definitely clearer now, +The plot in {numref}`islands_bar_top` is definitely clearer now, and allows us to answer our question ("Which are the top 7 largest landmasses continents?") in the affirmative. But the question could be made clearer from the plot @@ -1212,11 +1212,14 @@ The plot in {numref}`islands_plot_sorted` is now a very effective visualization for answering our original questions. Landmasses are organized by their size, and continents are colored differently than other landmasses, making it quite clear that continents are the largest seven landmasses. - -There is one more finishing touch we will make... We can also add a title to the chart by specifying `title` argument in the `alt.Chart` function +We can make one more finishing touch in {numref}`islands_plot_titled`: we will +add a title to the chart by specifying `title` argument in the `alt.Chart` function. +Note that plot titles are not always required; usually plots appear as part +of other media (e.g., in a slide presentation, on a poster, in a paper) where +the title may be redundant with the surrounding context. ```{code-cell} ipython3 -islands_plot_sorted = alt.Chart(islands_top12, title="Largest 12 landmasses on Earth").mark_bar().encode( +islands_plot_titled = alt.Chart(islands_top12, title="Largest 12 landmasses on Earth").mark_bar().encode( x=alt.X("size",title="Size (1000 square mi)"), y=alt.Y("landmass", title="Landmass", sort="x"), color=alt.Color("landmass_type", title="Type") @@ -1225,20 +1228,16 @@ islands_plot_sorted = alt.Chart(islands_top12, title="Largest 12 landmasses on E ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('islands_plot_sorted', islands_plot_sorted, display=True) +glue('islands_plot_titled', islands_plot_titled, display=True) ``` -:::{glue:figure} islands_plot_sorted +:::{glue:figure} islands_plot_titled :figwidth: 700px -:name: islands_plot_sorted +:name: islands_plot_titled -Bar plot of size for Earth's largest 12 landmasses now with a title. +Bar plot of size for Earth's largest 12 landmasses with a title. ::: -> **Note:** Good visualization titles clearly communicate -> the take home message to the audience. Typically, -> that is the answer to the question you posed before making the visualization. - ### Histograms: the Michelson speed of light data set ```{index} Michelson speed of light @@ -1251,7 +1250,6 @@ Five experiments were performed, and in each experiment, 20 runs were performed—meaning that 20 measurements of the speed of light were collected in each experiment {cite:p}`lightdata`. - Because the speed of light is a very large number (the true value is 299,792.458 km/sec), the data is coded to be the measured speed of light minus 299,000. @@ -1290,8 +1288,8 @@ by separating the data into bins, and then using vertical bars to show how many data points fell in each bin. To create a histogram in `altair` we will use the `mark_bar` geometric -object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `count()`. -There is no `count()` column-name in `morley_df`; we use `count()` to tell `altair` +object, setting the `x` axis to the `Speed` measurement variable and `y` axis to `"count()"`. +There is no `"count()"` column-name in `morley_df`; we use `"count()"` to tell `altair` that we want to count the number of values in the `Speed` column in each bin. As usual, let's use the default arguments just to see how things look. @@ -1324,15 +1322,6 @@ Histogram of Michelson's speed of light data. However, we cannot tell how accurate the measurements are using this visualization unless we can see the true value. - -One of the powerful features of `altair` is that you -can continue to iterate on a single plot object, adding and refining -one layer at a time. If you stored your plot as a named object -using the assignment symbol (`=`), you can -add to it using the `+` operator. -For example, if we wanted to add a vertical line to the last plot we created (`morley_hist`), -we can use the `+` operator to add a vertical line chart layer with the `mark_rule` function. - In order to visualize the true speed of light, we will add a vertical line with the `mark_rule` function. To draw a vertical line with `mark_rule`, @@ -1347,36 +1336,44 @@ we do this by setting `strokeDash=[3]`. Note that you could also change the thickness of the line by providing `size=2` if you wanted to. Similarly, a horizontal line can be plotted using the `y` axis encoding and the dataframe with one value, which would act as the be the y-intercept. - Note that *vertical lines* are used to denote quantities on the *horizontal axis*, while *horizontal lines* are used to denote quantities on the *vertical axis*. -To add the dashed line on top of the histogram, we use the `+` operator. -This concept is also known as layering in altair. Here, we **add** the `mark_rule` chart to the `morley_hist` chart to produce our final plot. + +To add the dashed line on top of the histogram, we +**add** the `mark_rule` chart to the `morley_hist` +using the `+` operator. +Adding features to a plot using the `+` operator is known as *layering* in `altair`. +This is a very powerful feature of `altair`; you +can continue to iterate on a single plot object, adding and refining +one layer at a time. If you stored your plot as a named object +using the assignment symbol (`=`), you can add to it using the `+` operator. +Below we add a vertical line created using `mark_rule` +to the last plot we created, `morley_hist`, using the `+` operator. ```{code-cell} ipython3 v_line = alt.Chart().mark_rule(strokeDash=[3]).encode( x=alt.datum(792.458) ) -final_plot = morley_hist + v_line +morley_hist_line = morley_hist + v_line ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue("final_plot", final_plot, display=False) +glue("morley_hist_line", morley_hist_line, display=False) ``` -:::{glue:figure} final_plot +:::{glue:figure} morley_hist_line :figwidth: 700px -:name: final_plot +:name: morley_hist_line Histogram of Michelson's speed of light data with vertical line indicating true speed of light. ::: -In {numref}`final_plot`, -we still cannot tell which experiments (denoted in the `Expt` column) +In {numref}`morley_hist_line`, +we still cannot tell which experiments (denoted by the `Expt` column) led to which measurements; perhaps some experiments were more accurate than others. To fully answer our question, @@ -1399,18 +1396,18 @@ morley_hist_colored = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( color=alt.Color("Expt") ) -final_plot_colored = morley_hist_colored + v_line +morley_hist_colored = morley_hist_colored + v_line ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_colored', final_plot_colored, display=True) +glue('morley_hist_colored', morley_hist_colored, display=True) ``` -:::{glue:figure} final_plot_colored +:::{glue:figure} morley_hist_colored :figwidth: 700px -:name: final_plot_colored +:name: morley_hist_colored Histogram of Michelson's speed of light data colored by experiment. ::: @@ -1418,21 +1415,27 @@ Histogram of Michelson's speed of light data colored by experiment. ```{index} integer ``` -Alright great, {numref}`final_plot_colored` looks... wait a second! We are not able to distinguish +Alright great, {numref}`morley_hist_colored` looks... wait a second! We are not able to distinguish between different Experiments in the histogram! What is going on here? Well, if you recall from the {ref}`wrangling` chapter, the *data type* you use for each variable can influence how Python and `altair` treats it. Here, we indeed have an issue with the data types in the `morley` data frame. In particular, the `Expt` column -is currently an *integer*. But we want to treat it as a +is currently an *integer*---specifically, an `int64` type. But we want to treat it as a *category*, i.e., there should be one category per type of experiment. +```{code-cell} ipython3 +morley_df.info() +``` ```{index} nominal, altair; :N ``` -To fix this issue we can convert the `Expt` variable into a `nominal` (categorical) type -variable by adding a suffix `:N` (where `N` stands for nominal type variable) with the `Expt` variable. -By doing this, we are ensuring that `altair` will treat this variable as a categorical variable, -and the color will be mapped discretely. Here, we also mention `stack=False`, so that the bars are not stacked on top of each other. +To fix this issue we can convert the `Expt` variable into a `nominal` +(i.e., categorical) type variable by adding a suffix `:N` +to the `Expt` variable. Adding the `:N` suffix ensures that `altair` +will treat a variable as a categorical variable, and +hence use a discrete color map in visualizations. +We also specify the `stack=False` argument in the `y` encoding so +that the bars are not stacked on top of each other. ```{code-cell} ipython3 morley_hist_categorical = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( @@ -1441,23 +1444,23 @@ morley_hist_categorical = alt.Chart(morley_df).mark_bar(opacity=0.5).encode( color=alt.Color("Expt:N") ) -final_plot_categorical = morley_hist_categorical + v_line +morley_hist_categorical = morley_hist_categorical + v_line ``` ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_categorical', final_plot_categorical, display=True) +glue('morley_hist_categorical', morley_hist_categorical, display=True) ``` -:::{glue:figure} final_plot_categorical +:::{glue:figure} morley_hist_categorical :figwidth: 700px -:name: final_plot_categorical +:name: morley_hist_categorical Histogram of Michelson's speed of light data colored by experiment as a categorical variable. ::: Unfortunately, the attempt to separate out the experiment number visually has -created a bit of a mess. All of the colors in {numref}`final_plot_categorical` are blending together, and although it is +created a bit of a mess. All of the colors in {numref}`morley_hist_categorical` are blending together, and although it is possible to derive *some* insight from this (e.g., experiments 1 and 3 had some of the most incorrect measurements), it isn't the clearest way to convey our message and answer the question. Let's try a different strategy of creating @@ -1478,14 +1481,8 @@ us to compare along the `x`-axis as our vertical-line is in the same horizontal position. If instead you wanted to use a single row, you could specify `rows=1`. - - - -There is another important change we have made. You will notice that when -we define `morley_hist`, we are no longer supplying `morley_df` as an +There is another important change we have to make. When +we define `morley_hist`, we no longer supply `morley_df` as an argument to `alt.Chart`. This is because `facet` takes care of separating the data by `Expt` and providing it to each of the facet sub-plots. @@ -1497,7 +1494,7 @@ morley_hist = alt.Chart().mark_bar(opacity=0.5).encode( color=alt.Color("Expt:N") ).properties(height=100, width=400) -final_plot_facet = (morley_hist + v_line).facet( +morley_hist_facet = (morley_hist + v_line).facet( "Expt", data=morley_df, columns=1 @@ -1506,17 +1503,17 @@ final_plot_facet = (morley_hist + v_line).facet( ```{code-cell} ipython3 :tags: ["remove-cell"] -glue('final_plot_facet', final_plot_facet, display=True) +glue('morley_hist_facet', morley_hist_facet, display=True) ``` -:::{glue:figure} final_plot_facet +:::{glue:figure} morley_hist_facet :figwidth: 700px -:name: final_plot_facet +:name: morley_hist_facet Histogram of Michelson's speed of light data split vertically by experiment. ::: -The visualization in {numref}`final_plot_facet` +The visualization in {numref}`morley_hist_facet` now makes it quite clear how accurate the different experiments were with respect to one another. The most variable measurements came from Experiment 1. @@ -1528,11 +1525,17 @@ The most different experiments still obtained quite similar results! ```{index} altair; alt.X, altair; alt.Y, altair; configure_axis ``` -There are three finishing touches to make this visualization even clearer. First and foremost, we need to add informative axis labels -using the `alt.X` and `alt.Y` function, and increase the font size to make it readable using the `configure_axis` function. We should add a title! For a `facet` plot, this is done by providing the `title` to the facet function. Finally, and perhaps most subtly, even though it -is easy to compare the experiments on this plot to one another, it is hard to get a sense -of just how accurate all the experiments were overall. For example, how accurate is the value 800 on the plot, relative to the true speed of light? -To answer this question, we'll use the `assign` function to transform our data into a relative measure of accuracy rather than absolute measurements: +There are three finishing touches to make this visualization even clearer. +First and foremost, we need to add informative axis labels using the `alt.X` +and `alt.Y` function, and increase the font size to make it readable using the +`configure_axis` function. We can also add a title; for a `facet` plot, this is +done by providing the `title` to the facet function. Finally, and perhaps most +subtly, even though it is easy to compare the experiments on this plot to one +another, it is hard to get a sense of just how accurate all the experiments +were overall. For example, how accurate is the value 800 on the plot, relative +to the true speed of light? To answer this question, we'll use the `assign` +function to transform our data into a relative measure of accuracy rather than +absolute measurements. ```{code-cell} ipython3 @@ -1569,7 +1572,7 @@ morley_hist = alt.Chart().mark_bar(opacity=0.6).encode( ) ).properties(height=100, width=400) -final_plot_relative = (morley_hist + v_line).facet( +morley_hist_relative = (morley_hist + v_line).facet( "Expt", data=morley_rel, columns=1, @@ -1580,18 +1583,20 @@ final_plot_relative = (morley_hist + v_line).facet( ```{code-cell} ipython3 :tags: ["remove-cell"] -glue("final_plot_relative", final_plot_relative, display=True) +glue("morley_hist_relative", morley_hist_relative, display=True) ``` -:::{glue:figure} final_plot_relative +:::{glue:figure} morley_hist_relative :figwidth: 700px -:name: final_plot_relative +:name: morley_hist_relative Histogram of relative accuracy split vertically by experiment with clearer axes and labels ::: -Wow, impressive! These measurements of the speed of light from 1879 had errors around *0.05%* of the true speed. {numref}`final_plot_relative` shows you that even though experiments 2 and 5 were perhaps the most accurate, all of the experiments did quite an -admirable job given the technology available at the time. +Wow, impressive! These measurements of the speed of light from 1879 had errors +around *0.05%* of the true speed. {numref}`morley_hist_relative` shows you that +even though experiments 2 and 5 were perhaps the most accurate, all of the +experiments did quite an admirable job given the technology available at the time. #### Choosing a binwidth for histograms @@ -1613,7 +1618,7 @@ you want to answer. To get a sense for how different bin affect visualizations, let's experiment with the histogram that we have been working on in this section. -In {numref}`final_plot_max_bins`, +In {numref}`morley_hist_max_bins`, we compare the default setting with three other histograms where we set the `maxbins` to 200, 70 and 5. In this case, we can see that both the default number of bins @@ -1692,7 +1697,7 @@ morley_hist_5 = alt.Chart().mark_bar(opacity=0.9).encode( ) ).properties(height=100, width=200) -final_plot_max_bins = (( +morley_hist_max_bins = (( (morley_hist_default + v_line).facet(row="Expt:N", data=morley_rel, title="default maxbins") | (morley_hist_200 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=200")) & ((morley_hist_70 + v_line).facet(row="Expt:N", data=morley_rel, title="maxBins=70") | @@ -1702,12 +1707,12 @@ final_plot_max_bins = (( ```{code-cell} ipython3 :tags: ["remove-cell"] -glue("final_plot_max_bins", final_plot_max_bins, display=True) +glue("morley_hist_max_bins", morley_hist_max_bins, display=True) ``` -:::{glue:figure} final_plot_max_bins +:::{glue:figure} morley_hist_max_bins :figwidth: 700px -:name: final_plot_max_bins +:name: morley_hist_max_bins Effect of varying number of max bins on histograms. ::: @@ -1813,10 +1818,11 @@ perfectly re-created when loading and displaying, with the hope that the change is not noticeable. *Lossless* formats, on the other hand, allow a perfect display of the original image. -- *Common file types:* - +- *Common file types:* + - [JPEG](https://en.wikipedia.org/wiki/JPEG) (`.jpg`, `.jpeg`): lossy, usually used for photographs - [PNG](https://en.wikipedia.org/wiki/Portable_Network_Graphics) (`.png`): lossless, usually used for plots / line drawings - + - [BMP](https://en.wikipedia.org/wiki/BMP_file_format) (`.bmp`): lossless, raw image data, no compression (rarely used) + - [TIFF](https://en.wikipedia.org/wiki/TIFF) (`.tif`, `.tiff`): typically lossless, no compression, used mostly in graphic arts, publishing - *Open-source software:* [GIMP](https://www.gimp.org/) ```{index} vector graphics; file types @@ -1826,9 +1832,9 @@ display of the original image. objects (lines, surfaces, shapes, curves). When the computer displays the image, it redraws all of the elements using their mathematical formulas. -- *Common file types:* - - [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) (`.svg`): general-purpose use - +- *Common file types:* + - [SVG](https://en.wikipedia.org/wiki/Scalable_Vector_Graphics) (`.svg`): general-purpose use + - [EPS](https://en.wikipedia.org/wiki/Encapsulated_PostScript) (`.eps`), general-purpose use (rarely used) - *Open-source software:* [Inkscape](https://inkscape.org/) Raster and vector images have opposing advantages and disadvantages. A raster From e479854718f72212368bae471cbe92779e3a3441 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 15:05:30 -0800 Subject: [PATCH 13/16] better line formatting in saving section --- source/viz.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/source/viz.md b/source/viz.md index 6845ce2e..6132ca5f 100644 --- a/source/viz.md +++ b/source/viz.md @@ -1858,21 +1858,18 @@ bad, while raster images eventually start to look "pixelated." > store *both* raster and vector formats. If you try to open a PDF and it's taking a long time > to load, it may be because there is a complicated vector graphics image that your computer is rendering. -Let's learn how to save plot images to these different file formats using a -scatter plot of -the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) -{cite:p}`faithfuldata`, -shown in {numref}`faithful_scatter_labels` - -Now that we have a named `altair` plot object, we can use the `chart.save` function -to save a file containing this image. -`chart.save` works by taking the path to the directory where you would like to save the file -(e.g., `img/filename.png` to save a file named `filename` to the `img` directory), -The kind of image to save is specified by the file extension. -For example, -to create a PNG image file, we specify that the file extension is `.png`. -Below we demonstrate how to save PNG and SVG file types -for the `faithful_scater_labels` plot: +Let's learn how to save plot images to `.png` and `.svg` file formats using a +scatter plot of the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) +{cite:p}`faithfuldata`, shown in {numref}`faithful_scatter_labels` + +Now that we have a named `altair` plot object, we can use the `chart.save` +function to save a file containing this image. `chart.save` works by taking +the path to the directory where you would like to save the file (e.g., +`img/filename.png` to save a file named `filename` to the `img` directory), The +kind of image to save is specified by the file extension. For example, to +create a PNG image file, we specify that the file extension is `.png`. Below +we demonstrate how to save PNG and SVG file types for the +`faithful_scater_labels` plot: ```{code-cell} ipython3 From 3a800f832f1db770ad570e7dbb019ad5f1691713 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 15:31:29 -0800 Subject: [PATCH 14/16] ignore altair warnings; committed faithful plots --- source/faithful_plot.png | Bin 0 -> 29858 bytes source/faithful_plot.svg | 1 + source/viz.md | 38 +++++++++++++++++++++++--------------- 3 files changed, 24 insertions(+), 15 deletions(-) create mode 100644 source/faithful_plot.png create mode 100644 source/faithful_plot.svg diff --git a/source/faithful_plot.png b/source/faithful_plot.png new file mode 100644 index 0000000000000000000000000000000000000000..a0e986de8dc6bba53cb89d28a8b37265bf7ec208 GIT binary patch literal 29858 zcma&OWmuG5*ft6(D2;R@($d|aNHER$0hyxX{JHzaH!Qze$HBFB7Yar2I*Da*JlZ9 zVNnshuWoTC%g;XIT)?A7VYDeF4JSG{+vD_>KZ~fssM2J7uA?C{lWUR6DGfckYiYQ< zd2p~*I2;<9rs3H=^%W))-k)9GuHHx=;maJk%PE9F-UeZcYU`sqk$%M#eZ_`P1&>S- zRR2c$8II^HhYxWt=z=iU0*oWNq_2*aNA#X<)qQRv7|K?%BboURe85w~q{5|8>_;+$ z_7|HHb91TU;^HbaOU@q7b7Jqk?DFsQ4NX@GK=IrT%6s_2$a&>560Szvtjkk00s;amTwdQH@(x;aubo6@9 zm$&d0NgKiQySy5UJuyBtx2JknQwidE>QkdWm$dDdm##!aMCY4nX(a6I9f6m34{5v(0pM?jMn;scBQQk= z!&`8pNlN!769=pWq!eTF=mcJWjoQ%o(SUqNq92W{s94K3EiVa{KlAC{?D0(GDLzj& z@!4@gxpy#=R&CJ!#Kc7Bl)9e0tu1pX2KknH+bwP?w@ri?FIdjKlqSTFKOcM()R#mQ zw5t)m?oVpiHdrF4MK*^K4~WZF@qLPwPvqymA;J9r8HE0+)B0EYP~xocDrNo zyOAxcUqdGIIafv}7amk^J;g;Q<;N+q55hE5qwd!H__OI{8aN@e?OVeiE`RV;S-32b zi1BctCx&99Ey6Jl%O{`U{S_OJH>|hX)9kmJgQpu-q;#0%NG}Up92yoZ zdQN?Mb~amMrT$m=;NXCwk6B14y|%ViDu@znT;^%h)l{C6)t|*bod#3~8f@3!Mum)) z`zH69*)4}+#BlHSVd($a+zgD!=ZiAm63<1r9roL9dGBer)5yilarI< zz@L6wHL**@&rkGKr^&U_>*}!LYAPLP+Q8D=qcU9Ij2f)=zh&;Pho>N8SH~8gzzyBm32PdU{$|R%W`` z=u)rEJU2J@r@#MMR!K=zRaMouhdP^ii4Pw>P<5og=HlYQTIQ7Uo&aw;V%=TmZb0GV zil5c8lnRmRzWSH(&Xu(6Wmbu?uogG-WL>GFR*`LSt_Y@`w;WD!BCe9sH!iD_p&uu4 zPGI}Vvb2cl>XP9_2K(J4`mO#eE=R2XsUs=*l4r@CvTn(f0YmT@&T6`3wWuIt?RKxc z*EF@oZ2(p<6=+Yy$BD@^HWUS7{|)%fdEi8TSxs>f|9Xi$vwN~~?haq!9@Qn0x=l3= zO69Zt{r!!Zwnr}E%-Bfg#;_y5r#m=KPEI9t^_T4hA`cee^VZ)THhrl!f8BQ7&GHs@ ziqaBQR5#1-EcrBqKE1O(oAbW+wfXyXjMV`@HkP5YZQDmx*)~^m4z4J%c^itA;T8T@>qW7VAA&Qi+1XayHl7t9iY$ zBZW2pdFfAOh{Em5MYs|=sr5!Q@pT%}9QtOrR(kVv2bJ!Wr4RVbb%9aFOcaNu=2R5i z5s{|u)=SoLDT1J1phK(Ci0LT&KwiuLsIvQ$CcCW2K&ho6p(EzN#AI4>1kSDn2+)^^W{w)LVl z2oXg8Vxc0QyXzHllQ!U&b^Oehio+;DDeD+d*GEp6!YxASKVQydvKYt6tQj|CLd|FJ zd{wg7zU(^@xHdM~eFco(e>H1=Ww#5mME^KeyHNeAc1AlLJz6rd{}VW}ZPfc+fvox9 z6MI~BW#C@qwzBff?{BiQQEbXTzpWdrq4P8i5T6tYBSYiikBce_vcnky-%S`7^u(n@ ze6aV)2(nLV0&g#}a|TQFesV+?uwB6ldKc<0q5UW28UJiLob9}9$j-nleWl=)ZQfFx z*F&J?`@O+u$HTNW3*czepJ>`$n?0%hUK#!K{@kB}=jkZA6P$8&ktZhg(v|cF7Xs7@ zTLEf26S-2E`Yo0vwg@mV(>xDoVRNR2gid25CvBq$A zS6Z_Gt6+X0R5G>R)kj(X>DRZjyN7p@mU9js-bNWuDpurmy3l~%6d1_JW6CtXPnWNS zGOp0m*DkGOFZN7mdL0Foww*N8EEj^V%p=*AH96U#ir{_03+(RfyrE(Hc=Uz?Hz#vu z=h`&T`#Fwi7~!$93G}g(Br9j;gW0kamXp{0Ickhb+v$yJ$bznBBcjDFQP<(QzFSQ7 z^(i~{$Kj)s+X}>*dVa4^QP`jPl%{&t7I5%3X`pQoE-JFvnGwm}l#5NY0VldY3gd~9 zTh!21PF)M`>Z`L2tCzD{V_mC_x*6UFZ{$gze-`#VKO-Vtw*H0(I_)4_U9V)@qWIG{ zZq(oF7e-vMs0?8-?CY-}r4~v!U?VZ-OlK35oa*baol@R_iaN0{rl@L!5rJHE!p$CF z8G5UVJ2^^_W~(P9n1f(fmlU<&IOT{)!mNa|X8m)barr^D^!kAk{`%hcQoV+q#b!-y zLuEY`4Tb~^5tHN*i3Dy>Ztn}tsVQ=>b1n{$DwV-$%<*au&;B}P*kP`+Q`hvt|JocS zBMa~Ql;N7cUwzbvg4Eka&xL&6N3Sa+AFn8``@8uLj}J_ZX~r=b|47{KD(ZJQisjZX zZab%=6DE5&rmo-q2P@J-R%cxEm7jO-&EF{#TKphHL#9XK9>>a2#t9zJMxAC!EjW4t zNPu@yw`C$Sn~vbeapOBRC=yiKz`}t4BQwU!;&~;U;NoXSmm!I=k?a~@e*EgKW3PC6 zgs9<&8(ehAsUiBbr5-*o>wfEaBHx>@Rqn<6UF!nwDG1BBX`GDFhlKS6i!EyKT z)m{87?qTyXOI33gi?drR3 z_}9?Buh~Sb#Av)itEC+$bP*jPYiSjC$@1e!60a!oS33JmVRtU z0|PQt_3#CBO5YNAdy+X1gfz>QtbPz${RsbS&R`=%*@;Qhv}AnJ;#co~g)zgiamu=J zYG3HNwyt>PYo+BG9vl9ahbbdUjNyb$d6M%=?&jX?xdp6B;BdMIsjFJN(sa9MdW$-A@j009t*$~3nRs*Z!0}yS^XnK!hd35B&ZM^jL-H4G{8XfprG)mu9s&Rw@U}N*95du-+vM4Phurg;+ zsOv4i7)NdX?gB~yy06H@9n-Ml_NERkfuq;SjrU(2-^bOtar(?B>o>4^ldr!XK@!8` zJSM=q&;bqcX_JY8I$HGI)2FjgFNMsSk9zrP$|1(BOKyqjq-6G=J$){sB?y(g^S$Z| zS9`zrBb#o{9t93MOTa$)R=DTcJNF05ZN-Sp+qB~NMR4w!t3DZ4VtCj$q~%g|JKd|$ zqKvQi3Yn=tVdA>X@pNzA>{2NnqjXsRhkZX|nnCSnrnznzQ9Y8841_2Zdjkf=*y9B* zB3O!$#x_i-cIF2ZYvbebLOVCRcb(T%(KuZ%_tLGKbwz06{m;cp9V2?zbBvXVBDV{k zX_il3VWfBbnmOeBj%l+x6kCnV&Va)4M~0A4op<_SNuxF_J+zu|ohO0$4YOx%!mO2> zAv=*&SagTMu>mI?#q$SAt6idvI=d(p-&vP>|8gBInJo*I*kp_UDnW#t#B=ciHnr0H zhR~0+^U64MO#Qj7rHM~`lV7t7akfpyId;aeRYtPnZ$6Y3fKnWhjcLS{b1meI>bXnq z{Muv;D{Ek#nu5O?Vbk8@;u_DGYIIsiN;OCAkWq*)1VHn10l}~RUZKSGXVBt4(t>Wa z<)x9MCI0N0cMpCxKHe88SUyi_le{r6kv%YokF??LV(Nlad_igQ_AW_gBF0y^C7~3D z%_d^+<()7__6tX`|2&cKiaRBe>a8Sf_+#7TQvE%*WE)eSyJ~5EKQh)l_#$R178U2} zP9=aiO+x&4c?i0I#y7t40M1lY#^lV7uOEx$CKg4+J{OR3v3_!BKQ!%aC9);nZd!cA zF7@2cntCgwGzFn;xaj6{KIf+^-+QmDZ=#Hd&yvow)$sM(Q95>MsyuA<-abQ5x5kiu zQ!3<|MiwjlcX3ZD$*u8XTpKK!@n~4+gp9MNbnux|?$c&A?vI5qSPT+0*@7yke zt-Kz?S2&%_O#S+cYm5(`n>D1(jRi7#!egV+BS$l4p6a;gR?*$tDC86P3jrKNIJLTs zNqaoZxwW-FgT;ffg=59}q)E`cpC)ED6?|5_sl+(pThE3C7Mv&Ko6pB(z5jjAM4r9e zh*vySd^%UuDAmM%1S95aw*?1Ds?OJ3*I`s%Op z7}lrb_g($B3jB8=J9Pre@NNHkH!nV9O__k*n7MK{Ki;g;(izce`@!=XZJWN zU6+Qum}D{>Lit$Ol;OD67m2_Q*(1{RwrEve67*@N)RCXjk$>H?2@!(3G#DiJ_|G3g z%QF)oF4h)%RTDz2xq8d%s>-SdQ$?=6ok8jPKbfRVtY!_-)vWR+)tClJIN&p4A`^#G zkGXcOBQ>6RU$2HXtj7qrZzY?(bwL8dIu|Wb6QA00w~~I6|8M;_Tw_G+d!F*LEPVd6 zm=TU~H+S*_yw0DjPONp~M>5^W@+et@l?~-^q#d{!O-s9HmaKYL&p%C!7pLouc^aBh z^;#xqSn~5O{f(v_4G@@J_^6)w@kq=llKqUl+&ddTmzxNccX8>Pda?tXa@UJ|m5A8N zl@#O4;J6NF8RYHS1UzX#9!yP5?Ju>Yz9e(k?|5WyD6r=UqjLW4>2=rludKHA?d*8ZD*hSApNF#2H!Fh@bjeO^B6E%^Gr=&koR0 z9qSMvm8iVccEO#UH207QWo1!KpKZ;}pUHUNHM5o<4&7#3eFXXm5{5!;78?^jhN3&H zw8P^uYP28JEt7>SfmvFj@cN@~J|T{MlOU%H+V}z&s1+<9e00qZNsJXea#|OT>`xYk zWh^zliYyLSgWX~cYLfmob7u}&ow5<)0;ne)S@#{!j*^Rl$WLi+X!FF7I|gTtn-Fb< ze6|d<=j@f!YkLoG0#sxO#E^}+GAa5mqiM;{@7%FW{e!m_c-gKmOr3a_CP$RAs6CmL zCs%A`INJ9vu&}W|D#TpsYiVgoOG&Nl7G#=E$s-`||=?vBaa?$6akE;hM^ z9=J`&^9&gmf?jIk=Ux8u^s~<`D`Fc&pMzK`j4KcsJrM)i_x{uVAN`Z&bAmG61hn=H zqx2t{$V_-GtPB|&yeM>|R&JjiAc}r@geKepkbmosX=LPES?IJvt~?OEZfowWLS&sl z1;ljWAMJ|Hs>~T1D;>x(z$`UeoRBd071Z7Gju$u@nDo z6iy71830Lf)Ut);^KDvts4b1Rhw&qBdvERR7T>pf@;5GOq>{7}-KnG4BEBund`ll5MYwQ$;Nk*Dpa<9>#6{Ys5LgM%A=w3%r`U}vsXQ}s%o z`L)#~Q`XG!v;qX}b~*KLK7Or4EIHcKX868DjGJ*}s%|^KH&jJom(bY|DMg^7#tbub zzBp0c$;)S-f1#a8y&TRA!(mK$!9j<&Ae~5xI!p-Y<=tfKb_vSeRf7D%fnAmx_@S3{ma zP#pg2Jyt|yOm?pMzmEj>kH`Hiw5d%uH<9Gel^R*&Wq)q(kotqB9fxB!^={(2CYiPB zv!w45IhiVwq^fGnWUZbC42KR&LLjFzJ~bpI=+$uerd1v`?$k-E3LNG8{J&!q|D9j7 z(9UZ>=}^F2@C*h;GEOCKpVtVi8!>i?OP=hIU$OJ@{>fWJPMz7>3;$=AIXS+-mxU3; zLx&9JGsb>W!jjgvepK)Zct=&uD*aZH@7m61bx_eg!fGs{f3zKxQ%;*2h<_l0>-H#8 znRNX|WY@{YDJJL4IS^Q8Mo`Q@80FfKCj1`*f+#FJ-SJW~^?VaxG^#EMhcT?unSzn> z)5I3lA2a6Cd6iH-S-1}#zwGS8UfL1UV}D7@?D2|T zYOcuCbm+kPRH!apL_oK3)*AsN@i7@F6FQ`Z?@P1nR>7%Tre?NYn_GL4+cmtB>h;kg zt-Fn#%bRJH@wjY0m8qECy?j>M=xmSh+ee1e3BFGppO7S#abzOchxZL6!(ePSK_~kQ$=m?rxaF-N3WSNTON$7JL&HCLQ7C74$OeG>m zC8)F09IRa@A6uzbUK=M(frrf_8XFdwV!lF0C=@BuRgmmI@FgmI6vf7#kRh z1TS3q!1=5QoNT|UAAXNAxO-{HP52S{14bM33-g^F3_d+iLsYbbhg$}@$i6=p#G>0B zXSes0br#_=#jp9Zt=hw9wtEdnu>d8UmE{-DK^HVgC!%R@ja^FYIu>W$Z^du#Rbv-n zTb`Kv_xl+}gHYBxDun1C=H^E;*l*r{aK%EHH5=h+8~bn4C2&pT`PYZBqTz#|wG%$7 zuT&wh*!8kML0mHVOPh6WRsYT~>zG=pWd5|QN*=2~a&@k`FyF6>wL$sFb-*-^b7Z+# zhF7MrrJ~+eAm=>W%0hx4GNshbB+vQ%s!%LdzT_QITiV9$nup4y%MPxVM6ZCR=OO*SX0AJNY5y4VJY9B5M;is+t^sz7I^?=Tp35> zM?A@?jFXWAdhcG-4$9#2_UU!?0rFST%+5Z_k$nm_wN$J!9#&?hR2xyimOy=H1-J6L z$L8ibzAg9r9Tkm-3V?>(teeSwtfS1D8f{F~dkhE|Kres}uYt)KM8P-QQ)j2$dlFK{ zwoWQ5D)(b4d#|pEcWSy|Sy9*Y83gh#!3xW$)LPs3a`2oL_rKu?HMy^n#BOIgF^7>x z!tEq2GdXJrg*W6niB9;csJq!~1qT^f=i#HbW43tt>-3q!#47Rv!n=+9-{2hr$S3n1 zf~@>6#mfz@4%`sPrx}QgFMUelU!MvDIiIpxWYW>Vz zbi@oc{e~l_;maVi^J>qNRgLrqglqYczX-7`d~czQxR;o&wS1f}3%m9-cvV%8y>(5W z5NO*8kc>=dE5oJRJUuT0=?IxU8Xe^vW-o9R~5(gaVbN_KGhcUbuF6TX@Q6R$sDWgSjE+T*v z5L`HyDK4B%pBWH+nFqO9{%dRReYAGoUuArtRYhB*^M9c{zaR_NJ&p4~K-X*;lbp=P zaNV(VUC#LjVet3UBxFnz?y*U>uMsk?GTKr?L$|fQ?C~|vCf@;0FZr{Lk7oceDF$Yh zPxVi-CS5n2tmW1G(t$D-KaJVJ?^zChp@1C8Ajs(zNtxeUzj0$IvK{9~V;Yp+I?L4b zRC9~@4iv_EeUE0}f0|wv|0+2A{GY0I&!Wq+$2nHIuvLn>S5LEIAj4SNq^7!-jAs+; z7gRV#P^J0tPCH$ZSMMc0q1Nes4q#Gx-DAkDw7gU>(r(K|{H4X-c;!w*RR^%^DqNNy z`A(r&9}iikO_5~V$7;X-82$S?LrGG-4ym@TSy|CH>q2JHtNQzZ%vP+|pmsd$ir%2{ zY75>Z27&=(&5>``umAT{KS7C|PUbg$~th(*4H#bmcZ?+rRnY zsy-Xu-2R-peb~(HseHrn{dbmbX@kYd?G2WZgdr&&$&>kSRi_ZN5nU1MKbybaeWSwP zq*CM+c-ZyKp8a1hz<+F2y}t7WhO%;j&jiG7Z+oQO`|Pj9iig9eCb1PHoSZBvRx$yPTK3AVfALZs!U&_VdIKFo<=qZeIl$ToD%x5t@k5UPE7`trNuAp@Q@h~f3Towxlca}Vdvrw zu!g?Y=N3B2BzD;wLqnH0EPv}yMwyVdU_*5R}0vEVE)19ohdY&?gK;y4JGni zLG|?x9W#Aj`ss;LDF*=#0My^piDY$6uNT}<0ieGJ3Q2i+-5KMEWt4MHAA*%#DtK3i49IgQz?&I+`ttl4%MhN zyuV9D`h7*7cHh&e_sWONiNcVneLK@H!(`y+s$)aX!Xu0vYdP|XjV9*#q^T(bwAonU zTRLUs?P+Hs(J-gZ>@=zv#MWhasvb=#=?Vu(4MsV>+D5_Z+09LCq6obHRL9`Ah64$5 z3{3_b!M&fxV*)#qv_U#wk^q>U7-_3wO|R}UM^~vqC`}1u%wndTN7K3*!Ar@(nxKf5-*K$3Tx37^ zD#n7T)8Wuf(Dvv!sI}ngnS-;SSzs7dJ0O|9dL1@|CM6{~ToICxbXk(_J{SSI9sF?f z0|Z$apwHIV@-!K?LB0Q2i-tiSh5@nYual7};bEghU$aBUUD}_MGZT|dr(MmN8XH@)Gmk57;PH4}Bcg~HJ(l|K0TBT0 z=H@-#gn~vmx?$N(Xd)wjfo!fkJ$&P$?YnpHcK-c)%f*$jwzlRVP;%`p?0c5FykgJm z4mNaAVd2V2FF7qGC1oz9oTetx;v%JvX;WjPl;VtU>RgQ#ev{h~x_}!X1}qjAtmCiq zC1Kv5qjOaB^ELY@InTWdPo+1;={{LkQR|o2Doetna-S1jTXma4g`2eO@+_dj*ABeZ z3hchP!u*~QJ_#3agRQUzSGsu2+60bKzx7wT@%1JXF-UmZO%in^)YNvGmcV(aa@tl9z9>>uR#gq|HXOs& z|MG><#KeT+&ThUoIv@aAuq&wf|DtdTSh(o}^JmbTFx&DH)eR?Rddy zq|#&n?}1lb`ERoaw6)pi&Q+~kSKiX{U989>Z*MI5b~tUu%CNxE2B3Cwi;BVlQ+IaU zi4l51gav$Ql@iH7+^cUE2GWgMB$BFX_01z(9}C*hxaMEH7@-XdSNznz%9@#^fF2 zuwtsc(yfN7J`DubiJ9QaZpO0a^m5+LsT)^4$foIX&Z^<4_0D}gn&q}9@`$(TI10?ucAF{L#vC0Ty?Hvol4%IzKAmp&f0ds#3zJhU(v8LSBy0YNA9NC6 zsD#a}0z>!5vc%_=fx%~SA3ZHmk+mW4r%z;1FD10iG|$PD_B zT(}88d9dTe`mA4ef^QSH5*R{KrXRkl5t_QEt={O5zqkgie%+bB@6VqV;KHHbn<=NI zpzseR^OOSE|7~`^l>jdR!5^JQR;Lcz$AZ1JyjkZ9G>2ffto{M+ch zirF3DVrof>LI(WJ@SmG##OJ`}v@v%PJUKo6Nk!vT`-MN=oc+v%BM!bKfSE zzz;}-CAvR2KJfujO*18szu~XW^JZw9n&MXoq^ha;d!TA5Xg2mmi-XhyPUph`@j{uW za|+|}+0wF;aT(ckDrx_W4Q%^&1@q75a#`#XS@ec~eGI)mO>8T)ww?IUp z7#2<_;P0F!Ek)7e(UAy*|EPrFob#L2;xzF18ZmSM6;ag^riF9x{oHUTra$54F7Gol zS(VyMiV=R9Q{xgl_{X=RF&a>2!15rS+1a^#IpeUi@dqFtq0H4U%sAqNy9VVUq>dj_ zs}Zijh7AEOwRo^XiNHm;O+`(8(nD+)gz5sEP06MtIf31?le=#jg~dM*Cg=mCc`_@D z7wzHgYDq#%)cDcVctn~!`CF~0$|l}dNC-0!NwesTM96bK{4xW6i}r|n=a@<7Ae9Ju z`jq;?*J`O%h9z&_!Z+2~bhmD`(BC`PSkF5N(`;Fg z^`qGp364~)PK#{!$Gog)x-=TtS?>hQCr}X1ieYY=B2?!E>6p4-?BjN-s=r!&zWVu{ z7{Q%ZVcm4#$`%z)A-g9s59YpM@D7PUnaxE~QdNaj%nZ+e^uQ;3?%)?59)5lv^b`VY zyc`C;63^=NwEBO=YqInKJ*nz*!AztBO>~T%vMqc0K4i^UmL~%8Ek&P_$>3gr_KXAT zdU$&}ctOweQ*ABBqmBt0bOHr$?1))a% zc+=xI^qx~cPhRxat;cI3hG^eL9X|g^cB#VYu=%fXMT{;Z23bCg4HRB*VbG*kC? z(X{W}oaU}uY9TR~($c|&XF~AmiXTrN%fr9zi8yYy12M#`#oG(FSq(|uL0SV zZ){4W%T8AyY~lc6C0)_7;i2)se!%*2HwH&|8YrH+j)SY zI_2~vxo@?PW{*>M_7XKj-4;pLtuI0w%P?nW$7ytI`wibP9=rbmHG^ZH0%MrT3zu&qCf`tpo+Vkd6R0yq@EG_uuQi=lnZv_0nN6qTpYpe*-g`L?s z+Dbs&&3Wy6mJb1g4YMxyc)+~gRK!2^$`|>vr*{wg<(5;}Z_I^6Cr0dV^HIjYi=S-` zv2Q89a#+%W@)zGX^MH805{QvahYYh)U&uWB{S+_53f3b0nYCtm@1kCP($i6XOp_Y~y(z`dKRhSn{$T&bu?|b0v<*`4D!TWj# zsnOjROXPZu`u2xxeEO3pr|mOF28NuH64b9FzL!ua-Z!utaNG3q@~kj%6ZU`bwq$In z)G79cCJd1j5f)u|fVpQkoXuKWc>H^QHC^DiLw~XmG$&vkjWXRFa7Sl@^OE%ka|xe? zpFlma`wC~*e1bS4e8A+O8P zv9S*h4s7^(-VT3-adB~hz^tXG+vY1oO-)_!x)FS$v_;@1+&~ zshX<(E1GMP&1eO z&`?uyN$jgtR8Je~(9xPvnt{};0i9XF>b9UHSL5+OKpk2qTC5BG<*xh3`usEBlbrB~ z6YMkrftXRNHYz*&Er94@-Pve|QIU~Gz}vk}rvBwipa1ih?yJH0_4lU(y$c2T`624M z9>J=`YHWUw_Z&^nfswzcwIy@8#Y5vLvEs-bMXx14Xd#R5LZP>3jU+|jWufI{+(P!k z#y9l0;07bnX zH5n%u*3iCo{Bp)=J&K;F6ePo{gY8Dxdp#3JYh7m(JpDs?zl!0va=hAU=IkF6gAT*t zK0Xii%a|CzIX_*hRa8_2uRl|!LqTdBvIuN0W;lVEYxifvztj|J(Su4D`Xh`!_sw!x z9Et%e^Kp@G43DJ{TjBC=IrcjS63Izh2hkR#-q=j>T%MIZ((k}(zN`* zJYQ!=%g09uogMUh-1h6=Q=|}kRXzgaqCR`b5HIMYCH9-Ke93iN(5QWrP-+s%r?D83 zO;@6s)Hlu{X^s6EYy3V-2yp8k zshG_;Ku1#t@BYBa9}#z-FeNpDdyqqehYf!qb~kd~PORi()05MG=@gaJOXk5_9| ze*XWaE+S@Nsf)hJi0H6nw!=5^GkSjM9UY>+AW;MLd-848Ep4oyKiCSy5MBY^q5O)9 zZ=fsMO$XqLYV-C$K4CSNEw8SL=+5*dyh=5XI$sg6Bp(_i2W=EBDfDk&~JfD=!Zr=T!3HLFZMyumii z7Cn?6EOWrlnzi17-Fa9_=&0>UIMe$S2`n8fj0E-t)L~r$HKa(L9<-i`*H+NuA>wJJ z<$P7<DxXjwQL!}MuGj6tu(6anDAEvgJBf6i5qn97YzG=*9k&`1OZ zjAgmZx0&xs(PGUIK(jF#1UXzEq%xnJNegXJiZ*~p;3Z^ki!sCL_rhfZfD#RsfWebaLVVwTg<0YA0}+HU$lQFNk+u-hw*}e3Qre zK@9oCy4=^`H2^`i3PEV`(f~dx8T-fK@m2M0Gl-tHOP z{XK9Me%MuXdbpT@I`4i0>&(+401!LUWZgw*sQ_-0m6CQ6cx_N-n?d@eM7tplL*)J& z7=g+B?j3xO1IeSj2Ho23bYjSa`tOd~MxZ0Y1F;~USK;IPu%`#0bUSE=*9Uff19fc= z2fK^m;2yu3HbvLMH-ip-pjxwL%bR_pR;JwtY(c!6@kG%Qpw2&k{v6!j(8p{ZrcSZA0HvG7F4vdCu$U3gY!yhonxQVdDG_7eAv`Dh==1urwwhRQsKzGk3W!k z?od2F9zA84W%_Vi-x)(c2%Zgc-0z!-fTUEQ+mZ*ZwU?F_F(D!0t)rxH(5p;R8gy~4m7Ad3y=X^AoY0LkeD9%sK_k{{QU z`$V^RU4>ZJX)wAAQbK9H1j!!K{!bdxV7sKKDEZoUE9rkCSW@1fv9Yh8wt7Y@2hC)? z*S}xCeV^VX@-QJ%q*Z5&_T_3^mZb;`knyCzasj!0@a5v?*K}#nrr3k+rDC8Hq=B#h zb8VXX@dIbdwl3ByLz2wHSY{@bfmSgiZ4Z-%>i@>QU{*0*@6V7I9GW0C#hq%iaNu_{ zpvUX-4<|i69flwPa6*mj?r8P7bKfhigZnSz8T)jN;pycCGikvlsXqBE9mM~!kE{23pR9+r! zM%xJuMgkO-mYRU+gi*ISN$=sGI)}R!n0=xlkbR-KuHWx$vTpxRB(hgI{A@M|>3GTO z$fP%h6!bnPaB=_kCdS9>ck@$wgboFkZ_t--hTS@*mtE&=C+y{+Hrr$I^~#Jo5>A(#-Ggt{v^l@s1~lR?kG z+&rwKqhmHg`W4&DOHwO?tM7(kKz`|}ot>T6!PgTdXdjAF!US*cZnqYIgO+3-3@ZhZTW!*Sy;n)XXt>nk{V#%5+c@ zh^wlWHEa>>*3fr(&`>YItRS)`KD5ZlyV7j;tL3<;4b=g-wRG5Zdg?Nc3)dgB@);;- zB4pJzB=+w`UjPcI-g=fjhFnBJS{kXPrA6E8pk~3LmlOvVcSyopH0%qG4Hwi@8Lt@M z1mrkU0VNuutt(t;p3=D!l=KBuQu$QwlRuQPnAq5zAdcsSh=}-SXlOIB?S2zRxFu1# z7MRKG1@WrO-y%B$s1HJk<6w7yh;jpXm{BrQbdhL%OMbsu1gLJ0Wuv?N%Yx5tqn22((*)?KwKgqYZsLN9Bc=ih-VB zWYSYio5Gq(gXQ>(deb4I){|(vodMUTbP+HrFlto1$@WL0FjWHvjW&;iHwtxX(GrWc)OGH8b#(6+nF%$TrRB$ z-2W!g9vYOVip)m&@?1parU+Z!yYZ)2*VYa@Gm3`2a!gxK&;e*ExO9NfC>`@Nmj%l4 z^5ens_3VgmJ_{(6M8w25y?*z-VTd%~&fk0qH%QPyt{P77XWmO3%4z9U5Vv^=*L`E5 z-E6%?B1t`L0yf9mnxSpuE{)HuHTP#t&HiYfCEi0e(DE|fPy1*sY-~2;WSLi2L&<6A z>FJF#3o<+-UXpt&={=6c`gV~5LqB5Oo1ml!u1F||Y=N|ONOpUF2ddi0xN#lFZ)48m zhJl~<4?V$jbV>5#&sF3`a*;-NoyR99t$drN|JMs3W@W_yV(C8;5-KewXi!5=o=(tE zQ7dfbxsg#&ZeVYO;d}fX)(1oPCuU}PZR!?#K$@NBp$RlW8F=o}VWWwE%?;=#nv68- z+M#cTv9W2|0ks_lt`sw}Th!H++sDVJ8R!-FTRd&6ofslie0&~*>j7GhbqkItpz4ea zN-zcS+M)V|rVY(*2Y*H4|INw8bpp!)XxX(Sl$9}CT3PKL9&QcsEW?BtScW!v96{H8 z6@lAkj&rNFAk#Mm1o00Ww#h)bAK6C-$djsWrkC9H2lPRayaGIqd)?k0yFWOQ{;dBB zdaDSaz8UdQU}@E`=;)~#J*OeA1yFziYvDB>|Fp^CX8`IoOr=MicfFWWKkGnYIUeK~ zVddo|V4qfc8Ikr`M>hBOBPn-z8*N;ZsAy=1`#Q+WV+XhcbeSOl5u9r;* zw%_@fB>0p<%@3FJMj1w9+2XgEq-oyL{vY`;!-=W2KYoap zB<7n7uIj}`muE7{_S~7keA>ZDw8_GYzcVwTea1v*`TMQ{U}@JgssXIivTX=X)b$L% zIosabo1dQtG3vogk<8XsVJs{xIQOeXw~y}zz)eZ2Z**yCI6pEmp2W>l{9kD7OO1?7-??7JDUFfxLakputkvChbRwYv0nK4utlyy* zM{Y1Muui>=B=DPr&oKxz9=1Aanivt^9av{0iv1I`X{|cm%6ye}C<0&hdKk~7el}VNmj)Hm^ z=<|*hjc8v%ic*b<|`QEn;xTmKHWxsL_2?;`TMB944#~*m#KsIrVY2_VrL6hOf z)P2P!prUGW1dOw47D&L!Ajd6tEMpH6N|Mi)kIr2hjADlnm{t)YYmz%H{S}7<;D3gZ!;QB!Be;3uyVzvp z7QO#I?)`wY7kfe6*E=m$-cZsL;g)6(Rpg<3q9mTYwlnVbfH@G&!s9;}t%ay^Imf2L zwFrv&pFQsarz>iO=Xbw_FXM^-8`!A>BycgFX_cUejl>Z=FrGboBh3G=I)+$h zt;B9rylGxRBI~n)jV$>9UM~B_>CVZB@iXoJOa`G#$SOG0Z$~84g#JIKM;}V5|E9-# z5eSphuZ*g)>)J&`q(qPqkq}ToT1q69ZbaA! zn=U~>QaTkBBvk}y1Rg-??v@Z~>26SZlbinLexCO|?--}X_{KQrhw+2WUiZ4^nrp^& zUDIIvk*p)fXO5m52QhyWahUplI(eR+xG$?)#g!%#UK&;Knhj1T8`{u(Rkra6ON;yG zoV?0q@%nvaKR~IdZYD|F)n=G2`Cq17y1jD{AMNn_uc3~T64u02cc%kIZRx51H5&~i zqj2`9pTuWi#-N8G{#spZ=w!ZV{W)}+th&;g$41{&?!3gr-t%-o@W3zieR7mN@=fLe z&GX(!`g31TS2{X`yP(gBXHl2g^Z|4dny%e?#F%=^`pZf1Lwyq6H(DHD>P)^pAV|l+dvpb&89DRJ1h1zeEeSA+u)O}jX zl^0Sx?R2Yh`N6XL#$~6>kEgNj%JM6A&gVz6Oq)Iqp|7KRNJ$r3PLhe1cPpPfo-r|} zl}O+&6>1&Ylblb(4vP5r*D`CBvfqOT4=}-4jzR4avY9F1I|1rop)WbzzQ#O3ch1k~ zq(+!NX2Z@rK647OD)Zp0;S7cD`*p;iV8QGT(-@Pop&ISgv*g=eqSXiHDrdhsw7AilGkz@yf9FoqbYd?ScU)-hJbHyX)GIP}czp*k zG@z?9Y-FysdMy>3l`T8vucGLWKaq|hYGMMMF(&(74vXE(Y>jNszCk`8{=*s|EWGi`#?k4tl;`y*wSsU zyo%r>T&7@2r5;5xo$EhKPVH_EML4w}7RX~VC2x{!wB>%U8a|>(<$f89rJ>}W~8jWEZ{lbc5!B7 z>1V8$)5og23<-Tw!kYp822yFHk9(_2Pmt;()!mm|6A8YNS zzAa)IqYQ#6nm_~l_e)Tn;Cun80urDpwa>bjo+_kFG>nMv&`jxqB9wkolP1MEf;;v$33%} zZu+ugq~3--%D9O8({p?q`}12HNjx#3IgD&k~30wOmYnH!s5I3wr zG+?cO^~J5T*c!*F>8N-051p)xMAna~@2+I3zJm}Em| z02Gu76>ccmZ2`LE53-r1IYQAUpkA=LL888W<2_dGgs!ep+GRQWiN}(r9lf{~EI+#Q z7mlMx8Jn<0WADGHkjea9R^{n6K1yuTbRy)~I>G)@S`Ux(QFQIY)tg~7qR;7&4ms)^lP+7GTU|b z)gM3D2Fk8S4;FCqMclpLqAhklNJCG$AS2!TFngJuHFxRi=2}Wsw`j@SsXmL_e&aa) zUip~;_bK`kK?jn|>nX}bPQ{xnvpcpU?aCkeAi4O$S3|-(?o4Jye){yO85&D3 zXhTSd?=*}jwr$kJ_Ae``lkYs--p^$+R7)IseYn-pNxWR3rIOAaVYhnj6Z`FsgrAag zSWP`&dYHb^9vOox5Bm9diEb*?(aLlzU3+Xlr$aZxXGcyC15@nit^{ zZl$KQ%PW{)xf3P8&mSjD<9jC^;XH{Pj&$>!izjnOZeQG)AGyeaWS>O4t+*{%q{WE1 zvmU6ZE_@hG+uE4@q}){aatvEpMWtnOGLb>*?fZm;3FG4^+~)eDmF(TkX~S%$xTvU> zel>{~g=(_0xKIY0e{ykV%MJw5h1cn2g6PY217#c*l<%=Pb!ptX(~9F{9WR%$ z9Fpy3ajYJvD;w|YS>&hC-nS5CAbH{SiD-p3LQX}Az1uS2H_5cEp#*bhL_(8di1Wof zp5D3_H2^rg9f*r;9@i+<${H)le>uN2*ZIZg)l=^)v)aNce$YrVov3gSg}ue3Apqu; zhib#PARW|Dqq+30_D1T@_VUN3zkmO(Q|IL#h*RNn>XGENtpKfTt|xF$07W;}BB^l@ zv2u!UtYT{n+jj-$*d6P!fxeop`kjlqZ0$5Z*#zEOeIm1Y-HX!@30xo);vM$4~A|*$GoE2HNWWu`fouI5t{{P3+<6m^T5KQ zr>8$TIbnrTnXm7vQy{(r6FMWEy)O6xL+Fh$*Uc~CH#(zqxgxqW5K*RYO*m7i!lI*$ zxdsJ$_q!yWihrh$;fjun#@X0daVB{)W(Gv``}RghB;UoBY3!K1;+OE6U`d%;vHmfA zf<0qqLBjdL-nSuiH@SxXuzX}+K(M=>K~VB#gAH3Dk-M~WZ;~eS)%}~kWj&vzbRV3+9nDbaaFOPnh@^-yu!NF(ZhZn7U*U{@UuTLzm_fySkb3ok<-{(SU=9NRez zsb|~DruTd<@U_yHhnjwuRWhDsS$7V*s*~HQY-R2~xfqP?&*;^A->}K30;l0m0ydXc zSu1kfNE*@j?ap(A=CIR@8yC}Y?DY9ltxvjMQ&mT@nur!N-g34?@~_tXR>uQ_L&#?9 z)#E{p`$y;Xt6fv`#TgwqLUvQ4D=RDZ&d!UwfBMnK$KVrRwzxWjI6!B@gGj*0@;I+( zW*>)A3j})~@87eX5Q2gQPpopl+qn8eeV_9YEhI8-P}+){2-m-Pm{8&^x`|MQ+@Q#d z=*A6769+w~V#P<#1=!d~UrlN0Iwu0Bb#CO1M|i?rSsC+%2AtFIVb=?>)-8My^yNXTXp>+Be zMH;rY?4vn!npX4c^AO1q5fRJt5faaSyl2pga0m4Uj36WM^P$%|ySdquDifj&7#48P zkYdEa_4Vm_e+D3<0Xgl}$k!v9Hb_k|C;M_|1~2?Z@V&TB2^Owy)lljIq+(84$~%?u z2z5PM+u494Pu_DDrkrSw;-%G4Jz{f;U%lH?8^`nn@WNZzg2RFjJQ z5cF`T*Z*TMazDAE0ZlqIT`Y&|*~dL17vD!qu@WDOBC(?_+7X-0w^^@-%|lhyD3dJ zY$c?Jso9wfBFpc^av$8wsASaTL-AXut4p8R9u@Au4a3To&v1%%=X~Y7IP=Ym^Y8*c ze=K@mv(BX^Ig?JqiBDi}snBunib_Q#@+|23CuFoiwzVZ=cDm)h3cfK7A9?@Cb~iWI zuVkl|DE?B8RsFPHH3|vXjY%8UU zhlUe}VO;{4?@N!)9G&8vJbH1jiWWyVHzBBj8LG`uH051#Tdk~(q}0+u(1yv9eO~G0 zo*_*;6*!rGcLL=(QpYb1y$#DpMk65*ZC zo&D4oxKvf8k3F;#3^$jWlG2v3f(Z6!)5J1M3SWXDe9a1eefS|Ga4O2eSHOThT8!bF zgpWZke%9I(+MDO@(jrq*n?Lg{Zo3{GdV16c6qG_-_u~VTPOXQ4Tm+qYa}cRsjoSz4 z*rCz1fXTz}3AZ_r8Uql%JpK zd7Y@Plm?&9a>ozOoMtm#hpoViLLws1BRD8zQc~LgqR83bwx9nU%eYQE-SQJTp_f>? z!X|6V$Hk=@P$IfyW6AFbSH!3(S&mPV9q$fnZdO`WnYpa%RWXRXw|s324U{zTJp0YD zPXOEu-J4r?@8Z`QsCm~O9{N~XvKkv3S5#L!?XT&?UuJ6#Q*D`0teP;{9;p84o$_f} zkRpYZO`L86x$Qs1YnpPFG=|y`e#&`)I;@Lw{v=MBIkc;5oj`ik-BR`%0l{=)_b}24 zOJGly>yw^U;@J-chK3GDFFWUIW3sm=ju@AsKBMe&X*pPTT10Dz-=0xU%{vWW=GDS0 zW7?@>=k)c%{3SOAk5n*}goMuR#SW24t{rTGJ~3Ghz7LP4V&b^ff^>W8Iz_qEF60Z| z@8=!~^owqbevw_}=dPDj9(a?16aSt_enM-;&{&LWuw=E=*8WSTqV%(8rWxiD9aV3c z<3e9}xW{Q)-Q*xM7-zc&tC|{zc(JqiQdEp`mryxT=Y8t<_8(loo>`B0?utg*1*d$i zc^e!|z#Jz^<^JaI(s=UuNUX_5vT&zK6*I%plh61(KuLj&!Y+SMQI@DoH(a3Rp`^kjF61j}_i&B-9D(O*77QvjjqpOn%rAf0`UBQx4 z@g{?V&S58}J$F;3yrJK@-0!XPTTh>gn#^qPtVE)ofJRJ%1OM@eGAxvnGpwYfgm@K- z(TjbVBw}!9f2xvYk}K-^j!SfDaX^r&G&(8O&tN&HE0xFlB?p`3v97K-*nvL_;d2|y zTXlxF+|va#2V+DU(QZ{AKOmaHm{Uzu-4LZ$#+WoCvrG zb)<@SEX7 zXS^`Be`7<6)0F}Kn3AM8ioq*1LLcPZW)^V}!KrK&s|mV!!=Zh6tZW1Xs;a8HS5bfX z_l+;Ue&AD9IuI8T7~algTJiYFn^e}{6M90ntV6Ds=0xV;j zhg(s$nIyH2h=_+QDsjyKC9o|15@3(xKzf$Yp8ciI(#Pia(wyW}i;`&Rl%B|a(cXB5 zzE^sde=M)vob9QNd^@u=XS3&Z@VIcUyF+gjea2mlIZe? zR;JxoImFrgj+w76{&Nkouej7K~(x*R`4!6r8}LH)#8wfX1hF4wAbcz_rE+SZi0iHi|lMrixJld4?s|}~j(n9mq%JkkYhB}$WLE0gxp!IP# z>-7(}Nd;DSzZv*+fr=;{{FzstTen|gPi4MUUYdAu8JzcTsL*t?wZ6BrHLqILbF-A?y)ZSjO#H&v z%T+v?#QR$Z&dA7!yqVeU2(kSb8M}K7(wpwT^?}GhXGKD`^u02*HuvJpMNz5&?x3RR zMJPRmEDZJUlz--B#g=+>X^&G(U@xzmGU|C1uZuRh1tD9Bt6x`FR?CF1^A*{;_RjfC z)10SopZnI7_I!To_>Re<(~#gJ?prHU>Nmd$^_d$YS=5Rwdgve=bL5;OZ8E-ucH3I< zO?O<&sU-WcRY5|INH6Hrdnm%uo2@2yNtxz`l(D-&CX%^~mWH+&y%1wdznRB3d{#p< z?HiuBthpf*;?4E6_D?rzUBVD@xcEAuj6H(Wvp`9o!^qj)t5GXFsdi8pY=dfT79tzO zdIp|3l1Prcjf^ByaA3$~CzrX|+B-aXF7Y|6Tru%_+*-wIr9gN9+H;k0nB;r^MkM-UJ4+|ZF>B?zUV`uW9&m&af?G2Hnb5`x@&E^FFY zL-~ekj*dl;Fqgehj^l6N@55)vL#i5^2Rc`34UHBz#~9JY2!|eP1m5G9{h9i%ZZKDo z2jr(Uwf5I81B@o{d*Ic`j~^v9H8lg*0c2%nW@gy|l_{g%FOS5k-jbv`8EoWDB3S~` zR!T07+_bCRqXek)d|?~iJfP7Et9t6sPwfDceg^~O?oJeW;JT5(DGBxvrVv{4wdGHW zlX+)vxS{MDd(Edd7X$?bO?aqSIXPP_`QblUJ0cD2It4}-Fd2UAeY+`&gLnaJO8AwV zo>WC7sUlEdcVFv{f8bq5F7qnjE7pYbFKqI;B6j8mdLQ(wiVC3@mFv@|C&Kj8^9uvD zZ_NSc0VOM=t(ivj(a#Y&hNCz*!3(cahDC@N)}CbI+b?y-N=v_-((UZ(%I$wKIs502 z0yI^*Yj>{!8gL$()Mn5Qs1?oxQT9Il;IXm+N&5Kvc67xSJxfVIg`M+(A2V%yeJMfM znZOU=Rv(j+ri|;$WGVqV7_<=_wk{;Tm$h_p&D#I+)iSqyA;`R1!){s>s$uf2t49CI zw;;Qe_o`(=?RAHdBnt>C_#qGoVEm~gk-NFeqKh zV>B9#0o;emGHGP0n$I@9DZ`V2j~D!8#s3JX!e$AG#T=f#1=G_l3sZUB{PLjXt4tgd#jBzu`SC%3lBLZ07KVEkQP zL17kB;r~1s_~;MZb?Da^?nvSW26qea^5%DBqUR?&q9q?Ev+#|8u;o||J5P@y zHC6;H$(9zHZh#>usHtt*153B!8A|`kyL{K%byFYE9&&T5w4M1+H{L?+y>1>B`Nw3On3^rR1iW!P)<*7~ z?IfRL*#qVh1w~|2-dLc*@&VMu&C}Dc1BB`&g?M1gc6VO_HF~7b^fG`S?QT49{Hx3d zJO!5MR>MVrZbrt${1~^Z?}DTJJOp_El6cuR+juX5rk?P?DdYQ5nG|h8LV`9VH88ZG zOa>2wcBN3b%MaV~|K?@?cWuP17zYigaK|=U*DVkNc%rL2ZhQ<7CItnBzOxg+29zUM zVUg08;ZOni-U6BskVxWkb90{o{51Q!cuPx*atzNaNX&|X4+Pbre?PaLjl(arzZ)q?DJJD3)+y`1|)H1Ox=a zYX?Bs#fT|v19(#1(9rJR8~O`7CpWjHt4l?9XmyWv>f)5o@%n9M=G5U;K+ivQ1BA(F zfw4bl6DcfQ_^~DeaeVX_#t1RGgWC)3Skf{wA3l6|WN%+Ec*@Sk78DYaR#HO#AMf+P zX=&FPNVp6%ex<`N1fO3}jpKiZIV;LZqOo9NBqSsc?d^G@qM`trLrP9At)W4dmzRfy zskZj^K3+#V%(r4nL?KQY>FJ|;$CCdWqYG2HHUsG7SYc~+>_$Z8D!o|Va0@25hQBg9Qn&bus23T+^>wAT( zZAOdUz~{kwsf6r}0_jrWDFQg*6WEaM^0{ppg*Y=XFi?&M64P2`cVM7wPj9F9VvPI$ zgX8(X*<$>izi*sU>)|3Os2Qxo+yu#y*RUzyG%{oEnc~8JVyaBQ>RHU%$Gk|J7H0e{ z(A8(hFw!v$8w?o$4pFNa@17ey#-nN3e8h^N6H`=FB*22X1#TW5Pqej1<}ssF@N^3m zusOK7+s4Orb@x%I6iCt9{+=>YHfItN32;}Ck4Wcyia?N3QH`v7?*shL!=tL@43+_b z!6>61e1U{XV`F1;Ypct|m$3$ra~u$`9)dz{7L=&1q@@F__^CZ>)N}##StRq-TNZrN z|8P{xvSB^UoDCQzl}_=q*@cB>2rn=Y>X71MK7ck~fYyDEc42D&-(y~^8<{t;;065z zl@3)vz;{G*nvNEkW9_aEM?fFM&;j$bbI1Y(LJ~|+K}5~}!(_6;VZLetGZCeNG9lS} zf7Bx1m1J_T-uHZ!wuwUF@4GSlAFnqNgt`l>9%i7#{F|NhgEe3%yjtV(j3 z!gw?nJ3G7S`dBHPu-WBhi~A^A;g>-G`uvcTr0U@zY-?*PEh{^w;)Ml#(i({KLG+W* z@^t^jRTP9w4~a%kBUB}%_*b9|%KL8=San0rsE9z91m<$a)!`4dhn^2`-=ihGn?SZP`tvJ2;O*NYP#gfS zh(FSE>Is;9_myv%ctk{IgINfhk%Gny)ZXsFi-@_34F7u`&)YxVSpgXh1Y&(^KLc^=q0!&Tx!VW^v_maGL`^}n*Ryj#lQEA{|Hn=b5q^wJry zdM*^xz!12;2eHR$!IZ%qK+qF~ffz@;x_+mw40=FWbz4n@aK);55mItIzHd8$v137g z{;za&bnxM?>S+UZ^`}}8MR9U)XquXu<|}wjo_dc*3fX?8{8Br7we&MOApv==5QFAR5?#qcT6v=&-3mAtB4@wDNEAD677LFRywFA#Otv6MZfaqJQqI5X#+zOGmZ$Bi=-JA{%2`6g2cU)RT`$d z9z~gGn5~5QmEa~!AVYGwJ`F`q2qSJ|sBifA_zTO+<1$`dU0o-KJ-*Mtguwk6^H8Bd zYy-eFe)`Rqd7PG%*K2!E52kzmQi#+pGJ7}KybR@exRxbOM*(*F zrSz=9$44Af1`U^8MGQOAZuC^P@S=-nwK;ZCfs>U6|dUm;Ps0-*WeSH;h z4wKD*G33?L6ZW}Z@)D2sr`Tc)&oJIW?}Kg@n|r6MI)nIfI-33zeIrwT~Sdn zBI5;RDp6>2jcspCR%5^+4h{}3r1T9Ke0oi7ZQq}la86%Aw}ZSOIXMDKCA_AsL=zJe zkTBnd$Q6xttA2MCGgFBrO)i32NJv}Pf*Umn2v<>92j&lES{L+=N$BWo@1wi{$_&>z zm4$@`L@h4>vWqjaafa~$YI7B{xJ}98-f_V2k9_~W0itj4#2A{+-jMECkwO0Qw!NZf zkR3ol78VvPJ6^qdb+X}F4^O6=B-##L3ricD+jsB2yQ`65IljhFS0-pP(tt+ImljIz zlT3qn1g3QXhBe`}(=`DR)0m)V(ACbweDU)WCwNfIqYEc?gU!jXxID)RDpp8IQm`W$L&FQ3CutM9qxQ(0HGuQyS`mj_;> zp;3nVJ8%QAIuTBgQ7Zsw9|$+TSUWQZ+uM%N`-xWVA)};hg(HB2(O39J!C_mUTb`ko z+znL`?NTcWK;gix@!}8$Vj+_CGx>nBY6cy$P@O zl<1=84XaDX$7{(ua*cJpQjkTS2tkQqVe1@2bmc)_qAKjd+*(scs6N(t_GBQCVp z?_LJXa4bUZnukEIxp3h^KtO=yI2JGP`$X$hQC(h1XW&Ewu|G=`aW&zmjumxxjL7es z^xOi;C`^NMWiT7iC+RJFnaZ(}aM6LAnyh1y+-<&sAk+-17+>t}?#3wb`?(`;_%}X${`{NF;0d2a_m}=Z98-(V2h{7%E>~X) z(?STEAmN)dFrW#F@?U7-t%YP|fpaCLruG;-kO}Grbzfo@e*RD3%L;%Cy$?eQf#yOb z;=+N2A!%Sav7;b_G6GA$@b()T9-f^nOIXvIdo+FjE)}oe1djxjx`qTdsPUGbN=iz4 z>{S9g09Ni-Xulbc`rfu zS$DL;9^ZmF-$w_VJrML_Vkn5b2I;>0KnO!bEBpzZPrSaNkf1vb8_h4+FgJ)wA(S2% z5`!{EMP(&KH$)AhxZHQ{yjfebfs&9KuwIjrlCS`2_ZCV;*LdnRfJDgszk3!Ip*IXPy~hVM)gyXglF z!!n>UAd$$-1F4{?kAL0=RHy~NEO-P4)DtIii##cD}#E) zY@EL9;?F3MH3$RWZCRW~*f$IphC?D~=5NE*9}k7S+4c1=qQ$gjH+~=q|xjD0-H9g!2kdN literal 0 HcmV?d00001 diff --git a/source/faithful_plot.svg b/source/faithful_plot.svg new file mode 100644 index 00000000..21282faf --- /dev/null +++ b/source/faithful_plot.svg @@ -0,0 +1 @@ +0102030405060708090100Waiting Time (mins)0.00.51.01.52.02.53.03.54.04.55.05.5Eruption Duration (mins) \ No newline at end of file diff --git a/source/viz.md b/source/viz.md index 6132ca5f..fd409349 100644 --- a/source/viz.md +++ b/source/viz.md @@ -12,6 +12,19 @@ kernelspec: name: python3 --- +```{code-cell} ipython3 +:tags: [remove-cell] + +# ignore warnings from altair + +import warnings +def warn(*args, **kwargs): + pass +warnings.warn = warn +``` + + + (viz)= # Effective data visualization @@ -1858,19 +1871,16 @@ bad, while raster images eventually start to look "pixelated." > store *both* raster and vector formats. If you try to open a PDF and it's taking a long time > to load, it may be because there is a complicated vector graphics image that your computer is rendering. -Let's learn how to save plot images to `.png` and `.svg` file formats using a -scatter plot of the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) -{cite:p}`faithfuldata`, shown in {numref}`faithful_scatter_labels` - -Now that we have a named `altair` plot object, we can use the `chart.save` -function to save a file containing this image. `chart.save` works by taking -the path to the directory where you would like to save the file (e.g., -`img/filename.png` to save a file named `filename` to the `img` directory), The -kind of image to save is specified by the file extension. For example, to +Let's learn how to save plot images to `.png` and `.svg` file formats using the +`faithful_scatter_labels` scatter plot of the [Old Faithful data set](https://www.stat.cmu.edu/~larry/all-of-statistics/=data/faithful.dat) +{cite:p}`faithfuldata` that we created earlier, shown in {numref}`faithful_scatter_labels`. +To save the plot to a file, we can use the `save` +method. The `save` method takes the path to the filename where you would like to +save the file (e.g., `img/filename.png` to save a file named `filename.png` to the `img` directory). +The kind of image to save is specified by the file extension. For example, to create a PNG image file, we specify that the file extension is `.png`. Below we demonstrate how to save PNG and SVG file types for the -`faithful_scater_labels` plot: - +`faithful_scatter_labels` plot: ```{code-cell} ipython3 from altair_saver import save @@ -1902,14 +1912,12 @@ svg_size = os.path.getsize("data/faithful_plot.svg")/1000000 - Image size * - Raster - PNG - - {glue:}`png_size` + - {glue:}`png_size` MB * - Vector - SVG - - {glue:}`svg_size` + - {glue:}`svg_size` MB ``` - - Take a look at the file sizes in {numref}`png-vs-svg-table` Wow, that's quite a difference! Notice that for such a simple plot with few graphical elements (points), the vector graphics format (SVG) is over 100 times From df6863891292982bccc9cdd3716e2e2d69ca43d4 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 15:35:16 -0800 Subject: [PATCH 15/16] moved faithful plots to img/ --- source/faithful_plot.png | Bin 29858 -> 0 bytes source/faithful_plot.svg | 1 - source/img/faithful_plot.png | Bin 150293 -> 29858 bytes source/img/faithful_plot.svg | 347 +---------------------------------- source/viz.md | 14 +- 5 files changed, 8 insertions(+), 354 deletions(-) delete mode 100644 source/faithful_plot.png delete mode 100644 source/faithful_plot.svg diff --git a/source/faithful_plot.png b/source/faithful_plot.png deleted file mode 100644 index a0e986de8dc6bba53cb89d28a8b37265bf7ec208..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29858 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 diff --git a/source/faithful_plot.svg b/source/faithful_plot.svg deleted file mode 100644 index 21282faf..00000000 --- a/source/faithful_plot.svg +++ /dev/null @@ -1 +0,0 @@ -0102030405060708090100Waiting Time (mins)0.00.51.01.52.02.53.03.54.04.55.05.5Eruption Duration (mins) \ No newline at end of file diff --git a/source/img/faithful_plot.png b/source/img/faithful_plot.png index fa93f603f2b4b297bccb96467bf6c66a4411a299..a0e986de8dc6bba53cb89d28a8b37265bf7ec208 100644 GIT binary patch literal 29858 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 literal 150293 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
|_W1)X&5@qSuI)NfwiDm- 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νx^Mh+4x(f+LGN%Mg@QT|_ z0dk0X$iHal;4UWJDKKMv_fxufrFyYd0AAGPXR>2wzulz|;!Ct8L*5rI-PK}J}!*hCOcR3{v=X58g= z;Eb9nr>Z7`wQ=;RzKDNLNCi+-t_ARs|8jq%WnX@abtz~FJS%`-g>yS)h3E0CK>K1m z@d)V?RadhtJ%y5MoPz!|4%?5NdWJ7IAT0^`a5ffjL*yxQ4It<=qKQ{cjyOsN8FsGK zj_jq;lF}w{ZdF-~PdCLQF>tty{${0l#}YVhJa8XBP-G0C5nZg8wYBG<%SN6S!X4o6Z<=otDItH1zeuC` zR;wC{gTDMaRxhTH9Ss8z!wT@LgX5slI^3KR(s^n`(aW6w2B6xl3sCJ53&)F=XuPB2 zt1#vS7=uehX0sa_r+riCKqBdW;{LuoO-t3_@&pKbBlq6dSj3L3*;VNyVZ&E{iI6~B ztx@fb&?h&SJy4Z^rL(yUn)B?Yt4{RoW!bnfW#|uT+IkzbXTbdAae-gWrb}No^PkWb z7UJB2e&?&TM+832p~wPB^58I_pxS~MdaqG%{^T7~kH_>0X>(g}Py^nBy~?nB@3p86 z3^$S8tQ)3*6pZb^+fvN~>yv5+#>_C2^2R-+r{OA&qYSrE+lil#bX0ew zzaTtM2c9H-kkLOuv7h9g4V7AhkxfV?;B#ZK_7^y@9bjdh9Ia{W!Gde-0Iw)AQ+0Ha%QyndWB`(S=f z-A-PjT?fTJa={&W!21YHIU@)p4=xUKQpetJheM!Iw_y>w&}`OumIKS^=!B2FVs*7B zTl%Xn6vti{<9}~l>K!AtSsj$b5EPeucYjFid5}$95PM-G)!uRL^u#K|n*V

ts0+n{(L)9Y1_T^)cj#MlZOBB^DVBmJX4B8YP*qTnBx;s>EUyKJEsdElgphZiewDFlnky5eFF4p$cOrD59n=2XT;}8pPgvX4 zc9KRm_+%GGn^Jl8Yi7jSM<*w_Fu|Z>Hy9xxp^VGVJV)9IKH9o6wI+gdY&oFO+S-a@ z;}}~#qIz73>g$^;|6}=htw09UI3AT-jAyPnLe7Pp;#H!Z0j00)ME~-6a!*&e0+RQK+K0yrv*3RUGTtA?byBy=O^iQEdqk5U1W@hNc zbFa>VuDsetQy(M1UzVTDZ8dp3yBXfv?mZRDXj{=Zt=~tx8(;b$e)f|N?Q9(iY1!O6 ztu}hk-(t6OG1S-81ley31`ziRuy2XepbknINz@u0fDA$I28XgB@)v$BvsU+-kNb)t zJ7XkdxR0)97Z0ukW;Tr&25*&`*$}ArpKbHd4okaGH6|Wa2mD6cA8z$o-W$+>syn6% zYWM}Q=Q8T~S{>7;MB9SGjz4#Av>0<|>Q|M(}B_A+@RX~mj_gA`=^iNu_e*)MxC_~uZXsQykR!%J;8|$&Y zBn0?tj)4ex%=(J=Vq-@R9jg~;n-vWI^BH`9G`OS&?ysuUt-OQXS_8hpDh@XwBUs@8(^2Fv!m z{Wkdt7#&s`s1f245@AOKBeb{kHDLeo$;kW!WQ#*lc<3ew38QFsn&dIr?qn{1lKGr{ z-8!1P72(U|0RA}grHxdoEXAbEm zS~!FSGCP`L%tKzt)rVx^TRymwU%eyo!#5O>9Gfz||PP&9E_%QR5*&fU9C{ZFgGaLoc90GC<1Gsw`Ntu$!mCE@w7q#Zn=|SRE|`>9sO{lEL4t4R1l2TT_SnFR@EBxUFT4lG zi%*mOZpLY;C{jG*-;yB~AfsS~1BN?%Uvd!kFP@8*V078%K6*t~D&9E^WDcfu;thas z3-7S~Sm=TOzP0g7b}yHOi?)EHpZKN9!PBYy)K+{{%1NGNy+@(uzw%V(@J+`2lfJ zSgkn?GWRf8L!rg!`@~)l-oD)yjcL$!as<*(VMG46KjnQ_?XPLS8m1 zMzUv0_cav0C@NqJlQaV651N-Lw)-h@+ZXB>%4FCbjt1nVTJPuJgX&WY|xZcOj*=g#a zrBpSeF2+xe|MwTOfcxVhxq+!)KYMPWhQuP^3pv2DRGXE+MKFi~+r}ThRp^CDNF41V zh9OwnEuz-s$9Qsy+-Ai8Ya-1-obrOrd`SPX2UjpMUOc*=qUDptgV`AZ+Jy`51hIl5 z$bu?;kMWUz9Jh1F9~Ccbz*0h@M1+i~y7Qi_cj2}}ao3%M})N~oU~V%6XO z{-4zmQ{zDr`!e5vNciNUOufeb{cnXqd9~tN?K2XlZyMyE0LqzzL(cmie9u2ovZwd} z$V13Yfp74IoCRSE=iEEFEtYrUt#sbXakAFpU{CC|l4M7!i=T>r`S0@LY*wp|VYfvT z!$t3dwt0>1-(0c6w+R$by0j!dEyw^gYA27d->>k|aj zJpbzBU2X$hMUb~yX|qB@h<|Fm@E$0koAsXPy0yOo9rUU1vKb-yTz$fIR+^s)XjViw z6DZ|VKZyM(sYJ7mN^mohYZy-5Z1zB(3si$dTZI>dLp+5&TjYlK6?b=`yrP7zix)uu z!f=GdR`%sbCDB!gSJ}V+_WRWj3jw;nL&lqO1qm%K-I{~AOEfQJ_!I$QbPln@lbk^p zWryJ0R>5;$k{O`P?iU4MC?<%SYaNXIP5C)5>4`~!^%$z zXo}r17mox4K?e2^@hV0Ptmb&>C0*;QXxhS7PvCqA5?`PMCr0}F`dlwxHco$k zMCrE^D!!SRss{*YSi&^JOWP2Dca;9n0U4_EFtlFa8e*Va_u{?S30B~%bd*9K05y%;MiMv*9?amdKS6OXv`0kVO=bi24;cf10Hi&G2%8( zw}@i2r0>hSWbmwD^HrUhA_D^4TFbI*5SelNHQ#Cbv^(I1gaqIc9JT4R5ytRb2vJ8a zratn)X#{>m!4lwQy{Edmx^SW6P)>joY0$0cr-p6%paZi7hQZpED900t3;kSAV(W_u zAPL49hgNvKg*~igKD5t?KsOt$z(trfwlAf1Gm*Itgv8V2T7xR<>-+Sm6Xq&y!|?yc z_?F|>1EaamW*eyabIRI$%vw;`Awa;N=>qhX%hYiC(PE56!WUQ7a=x<9Hb|_p?J}Ji zE8#S;Mak7t1~>WLm*vR!pT`-C_(smk7sdvFTpb&BPk%fUu+AVdumWw-OpaaFecg-^ zgOk62!_{=6qp-e^+=9lg1z!Zu0bjV&ICEe6@^H)0D;ue4Xe9agUPnf7;N)+H$KvhV zw?TRmIR6BPkf46$`Rz|yrCkV^$MxvC!(7C z*w~7~RjG)OH3|n|8bQ%gSvXxN3ZIY__c`Nus*66T371vyDT~qHnx38qYyg|r(_$N- z3Ws*WWhC06jFi=3Eppk~k6>MeW#tKIp@DaRtB;nPGXaGM)?EUSCB7a3bAh+UQzzH9a z-qwIJoc$=e2mSYR{)8xQpEj%NPEk?K8CMVk?*-sh^>;Ra&;@{@kIY#mrflO?|QOZzH!=@0_4i~rc@Y4l7dHCiu&a7HnB>3n(&f}i3VN*)&;J3j?H&)u*z;*sAY1 zx{C848R8W_ogD&ji6Qq}Nh7Yrs&UI676@E+g7SQJw>1Y43KC<+KVOMjd21C&K3w4O zN=i157}cf<3Q7pR@LE&nieW+$r=Xb`nJPVDv`wiAbt03a>g$c1PnZ-l#(0f z_`%NVc2Er4UBx?>aw+{d37PH{_|xIX)$iNH$(c5!Lnxl3Hh*6{Goikl7vNA(?b1cK z>=k^N$iW)=|84_I*A4xTjpcD*#x<8-IuB|GI8)&E zzN#u2i{D`ymmy?**Iz&we#2*z#_HEm<}*u+nbTAM^in*&OT~zeJ(ToI)2???3}j2; zO^OS@vE|YAW$XU*D4$~6xX$OmDT`pX9F-6;-l1z^zCN5849S8P;YVsLyfrRq)utF1 zZ7=x5-RmM_!tHF*UV3Tz((lRN9Wmc!AIyB{xcq5ik>~B}$Sni=*Hqkzj*jl{cs$Ac*c%T_4!}(VBo>w4bCzynbY4+x<;;%Tfc?3_%w~d3zqM~ZYVrGR= zSD4BIKW8?v$nZA?E z2mne-HOB*8^?vo%Yu9Dj^78W5w-~S@!7JP+cUBj@6Z-csYApRcFL%@I)>Gz<^3FC{ z)7aORNB+1%7535`zKBR$iC!K+Dk;XcH(gaZbP>lsThVq3C4#31Z!)pyx{`fEwN?N8 zVo`;a@+`4pt9LeF0!>fOuKtbtY!vqhAJ((NEWBMUEiHT8z)UZj|L(NkcK`AH74o6= z9!X7yr8}QOAvwwqkp*!j#EM3*32EIAROpx!^Z;8_djK{68a;?HOMjfOYJ^kB#jq`2o$d^JLIva_U+m-HO3LK%xabN>-nq|DM_8 z)S|0TIFon;NDY7~&2-|A>$fuTABl|?JEelO_ea4QUIzR1Fh5G%`R}owk55e%(v5kW zx|bCXq{19-#NT!c!;8VpjDcP9aaPuOfF5|On8JWv1WWpp3x9`HOs_8g0q>#w8A6$D zkGTnjVjF%Qd3RF>4vr0?aC-c3>|$_6ygF0*eTL%~D%bTmAfHH|PFsrBEZiJWucIe0 z3(mwQ=nO{n!TbsmJM`Hdp!)hW=x3(bMnKp-cxT~fnt*cjl7`Acr{#d$ARbc|nd`P? zL}@>myPu* zIFc$XUFrw$$e2!GpkhO~{VF`#n}2^aK*yIfq9sk$YcPjxpeFkcO48>BsZb_u*G%Uc4N(DV*Re*$y%N5uF- z2%D@zjG?*BJ^34lRb}tk?S{oz&fCax{^3%$>qB3E|8jFg;gwMbn9;8QieaRM?&BF) z#tqc+gSLZwo&Ac_KCpHHe0q%|Rv2Rc0xP|MZO%7MMitFDB*WjF0j6{~)DJ8E#yo1lgX45US;!!{k3+5#`<^RY2_ z$;Nm}Ph3H=NJ)3}*E74C+nfC$C;Vni~Ym^ z4!um%-SD!2PYwDqqZKpi=H@iiUa(`3g!>Vca&e#?r>3Uv3_c6RX=-X4>Q*F3NJAX4 zm`;8G#DQUnJLY~v1ut&^mR{F|Dz5dK${w>LHBw`dP^kx%}93$)Ycljmchj`29JdmK8Fh!G9Ak_oy zGoV0sErJ0F+VI(yptJyze#e2vf%+7=x_X(T!X`wv2FtqpEhVp^5*R1!5>{4|q1PaR zj(jRpQW*POb`Q>nVjIzHd`}ub9h^y z_{l8+cLRVnz%~j$Mxl#ZHq~~H4Al5uIt@P?MO3~>MAZTqVF^=)b02^_vc#R#Ni2XJ zm|nbnJD1qW7p*4{or8=&=UwSS$Ct9DL$a$cED}@zF`?4@jCv)Wd-F0O^u)! zw$;RB0<(TzPi_rR$U%kNJhcRBvgaeuzOjFRP6Inju)zU`-D0H)sE#-s@GLR^qxb?L zNFf_f&gXVT#qT`{Q9Cb^7~AXIvnYNUF*6U-mM?07nOiGpvs_467$oktLcH9o=hNsY zo=+E!z - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -2 -3 -4 -5 - - - - - - - - - -50 -60 -70 -80 -90 -Waiting time to next eruption - (minutes) -Eruption time - (minutes) - - +0102030405060708090100Waiting Time (mins)0.00.51.01.52.02.53.03.54.04.55.05.5Eruption Duration (mins) \ No newline at end of file diff --git a/source/viz.md b/source/viz.md index fd409349..8673d534 100644 --- a/source/viz.md +++ b/source/viz.md @@ -1885,19 +1885,19 @@ we demonstrate how to save PNG and SVG file types for the ```{code-cell} ipython3 from altair_saver import save -faithful_scatter_labels.save("faithful_plot.png") -faithful_scatter_labels.save("faithful_plot.svg") +faithful_scatter_labels.save("img/faithful_plot.png") +faithful_scatter_labels.save("img/faithful_plot.svg") ``` ```{code-cell} ipython3 -:tags: ["remove-cell"] import os -png_size = os.path.getsize("data/faithful_plot.png")/1000000 -svg_size = os.path.getsize("data/faithful_plot.svg")/1000000 +import numpy as np +png_size = np.round(os.path.getsize("img/faithful_plot.png")/(1024*1024), 2) +svg_size = np.round(os.path.getsize("img/faithful_plot.svg")/(1024*1024), 2) -# glue("png_size", png_size) -# glue("svg_size", svg_size) +glue("png_size", png_size) +glue("svg_size", svg_size) ``` From 35f6828bf451c4bf5163527c5cccade9467ba686 Mon Sep 17 00:00:00 2001 From: Trevor Campbell Date: Wed, 4 Jan 2023 15:39:41 -0800 Subject: [PATCH 16/16] done polishing ch4 --- source/viz.md | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/source/viz.md b/source/viz.md index 8673d534..5522124e 100644 --- a/source/viz.md +++ b/source/viz.md @@ -1880,7 +1880,7 @@ save the file (e.g., `img/filename.png` to save a file named `filename.png` to t The kind of image to save is specified by the file extension. For example, to create a PNG image file, we specify that the file extension is `.png`. Below we demonstrate how to save PNG and SVG file types for the -`faithful_scatter_labels` plot: +`faithful_scatter_labels` plot. ```{code-cell} ipython3 from altair_saver import save @@ -1900,9 +1900,6 @@ glue("png_size", png_size) glue("svg_size", svg_size) ``` - - - ```{list-table} File sizes of the scatter plot of the Old Faithful data set when saved as different file formats. :header-rows: 1 :name: png-vs-svg-table @@ -1919,21 +1916,17 @@ glue("svg_size", svg_size) ``` Take a look at the file sizes in {numref}`png-vs-svg-table` -Wow, that's quite a difference! Notice that for such a simple plot with few -graphical elements (points), the vector graphics format (SVG) is over 100 times -smaller than the uncompressed raster images. - -In {numref}`png-vs-svg`, we also show what +Wow, that's quite a difference! In this case, the `.png` image is almost 4 times +smaller than the `.svg` image. Since there are a decent number of points in the plot, +the vector graphics format image (`.svg`) is bigger than the raster image (`.png`), which +just stores the image data itself. +In {numref}`png-vs-svg`, we show what the images look like when we zoom in to a rectangle with only 3 data points. You can see why vector graphics formats are so useful: because they're just based on mathematical formulas, vector graphics can be scaled up to arbitrary sizes. This makes them great for presentation media of all sizes, from papers to posters to billboards. - - - - ```{figure} img/png-vs-svg.png --- height: 400px @@ -1952,7 +1945,7 @@ You can launch an interactive version of the worksheet in your browser by clicki You can also preview a non-interactive version of the worksheet by clicking "view worksheet." If you instead decide to download the worksheet and run it on your own machine, make sure to follow the instructions for computer setup -found in Chapter \@ref(move-to-your-own-machine). This will ensure that the automated feedback +found in the {ref}`move-to-your-own-machine` chapter. This will ensure that the automated feedback and guidance that the worksheets provide will function as intended. ## Additional resources