7 Reshaping data
So far, all data wrangling operations in this book operated on the rows, columns, or cells of data frames: filtering the rows, selecting the columns, mutating or summarizing the values.
This chapter expands on this by operating on the overall shape of data frames. In the first section we will use pivot to turn columns into rows and the other way around. Next, we will use join to combine multiple data sets together. We will use these techniques to look at the relation between global temperature rise and greenhouse gas emissions.
Chapter objectives
At the end of the chapter you will be able to:
- Explain the difference between long and wide data (Section 7.1)
- Turn rows into columns with
pivot_wider, selecting only the key and value columns first (Section 7.1.1) - Turn columns into rows with
pivot_longer(Section 7.1.2) - Combine data sets with joins, and specify the key columns with
join_by(Section 7.2, Section 7.2.1) - Choose between
inner_join,left_join,right_join, andfull_join(Section 7.2.2) - Recognize one-to-many and many-to-many joins (Section 7.2.4, Section 7.2.5)
- Use
semi_joinandanti_jointo check which rows have a match in another data set (Section 7.2.6)
As always, the chapter ends with a multiple choice quiz organized per section that you can use to test your knowledge and understanding. You can also expand the table below to see which functions are covered in this chapter.
| Function | What it does | Section |
|---|---|---|
pivot_wider() |
turn long data into wide data: values from one column become new columns | Section 7.1.1 |
pivot_longer() |
turn wide data into long data: columns become rows | Section 7.1.2, Section 7.1.3 |
geom_tile() |
draw a filled rectangle at each x and y position |
Section 7.1 |
geom_hline() |
draw a horizontal reference line | Section 7.1.1 |
inner_join() |
combine two data sets, keeping only rows that occur in both | Section 7.2, Section 7.2.2 |
left_join(), right_join() |
combine two data sets, keeping all rows of the first (left) or second (right) data set | Section 7.2.2 |
full_join() |
combine two data sets, keeping all rows of both | Section 7.2.2 |
join_by() |
specify the key column(s) to join on | Section 7.2.1 |
bind_rows() |
stack data sets on top of each other | Section 7.2.2 |
add_column() |
add a column to a data frame | Section 7.2.2 |
geom_area() |
draw a (stacked) area chart | Section 7.2.3 |
semi_join() |
keep only rows with a match in another data set | Section 7.2.6 |
anti_join() |
keep only rows without a match in another data set | Section 7.2.6 |
Data used in this chapter
This chapter will use data from the website Our world in data. This is a very rich resource for various country-level facts and statistics. In particular we will use three data sets:
- Global Temperature Anomaly, derived from the British Met Office Hadley Centre HadCRUT5 data, contains the yearly temperature from 1850 compared to a pre-industrial level baseline (Open Government License)
- Greenhouse Gas Emissions, based on the Global Carbon Budget (Global Carbon Project) and National contributions to climate change datasets, contains the CO2 and other greenhouse gas emissions per year and per country from 1850 (CC-BY license)
- World Bank Income Groups based on the World Bank country classification gives an annual classification from 1987 of countries into lower to high income groups (CC-BY licensed)
We will be using smaller subsets of these data files that are redistributed along with this book. If you’re curious, you can see the data sets documentation which contains the R code used to download and preprocess the files.
7.1 Pivoting data: wide vs long
Let’s take a look at the temperature anomaly dataset:
As you can see, this contains data per year for each ‘region’ (hemisphere or world average). One row represents a combination of a year and a region, with values for the temperature anomaly and the upper and lower confidence interval.
Let’s see if we can recreate a version of the famous ‘barcode plot’ on temperature rise:
(Note that to produce the colors we used scale_fill_gradient2, which controls how data values are mapped to colors – see Chapter 8 for more information. The neutral midpoint is set to .54, the average value for 1961-2010 also used in the original. We also use geom_tile, which paints a filled rectangle at the specified position – and since there are so many years they come out as thin bars)
Long to wide data: pivot_wider
As you can see in the graph, the data is split by ‘region’, which contains rows for both hemispheres as well as for a global average. It is also clear that the northern hemisphere is warming up more quickly than the southern. Now suppose we want to calculate the gap between the hemispheres by subtracting the Southern anomaly from the Northern, we run into a problem: the values for north and south are in different rows, while we would need them in different columns on the same row to do a calculation.
In the lingo of tidyverse: we have long data (variables are in rows) that we want to turn into wide data (variables are in columns).
To do so, we use the function pivot_wider:
As you can see, there is now one row per year, with three data columns: one each for the unique values of the region (names_from=region). The values represent the temperature anomaly in this year and region (values_from=temp_anomaly).
We can now plot this difference:
In the code example for pivot_wider above, we used a select to drop the confidence interval variables before pivoting. Why do you think we do this?
Remove the select line and re-run the example. Does the result make any sense? Why does it happen?
As you saw when you removed the select line, this is a crucial step before pivoting. The reason for this is that each column that is not used in the pivot (i.e. the names_from and values_from columns) is treated as identifying unique rows. So, each year is given a new row (as expected), but without the select each temp_lower and temp_upper value is also seen as identifying a row! So, the result is not one row per year, but one row per year, lower, and upper combination. Since the lower and upper bound tend to be unique, this means we create 3 rows per year.
The lesson here is: before doing pivot_wider, always make sure the data only contains the columns uniquely identifying the rows (here: year) and the names and values columns (region and temp_anomaly).
From wide to long data: pivot_longer
As you might expect, pivot_longer is the opposite of pivot_wider, turning columns into rows. We can use it to ‘undo’ our earlier pivot_wider:
As you can see from the result, we changed the columns from north to diff into rows, creating four rows for each year (the original three regions, and a new ‘diff’ row).
Here is another example that achieves the same goal with different syntax:
As you can see, rather than specifying the columns you want to pivot, we here specify which columns to leave alone by using a negative selection: placing a minus before the column name(s). We also explicitly specify the names for the new columns containing the names and values.
Note that the new column names here need to be quoted (enclosed in " or '), since they don’t refer to an existing column or object.
Plotting data with multiple columns: a case for pivot_longer
Let’s have a look at the greenhouse emission data, which shows one of the common places where we need pivot_longer in our daily work.
Just to explore this data, let’s have a look at the rise in global co2 emission:
The CO2 dataset shown above shows a common pattern: for each case (country and year) it gives multiple measurements as columns. As illustrated earlier, this is convenient if you want to do calculations over the columns, or if you want to investigate statistical relations between the different columns.
If we want to plot them as different lines though, ggplot expects them to be in different rows, so we can use e.g. color=type to plot them. In other words, we have wide data, but ggplot generally expects long data. For turning wide data into long data, we use pivot_longer:
As you can see, we select the columns to pivot (co2:nitrous_oxide) and pivot them into a new type column, keeping the default value name for the values. We can now summarize and plot this data:
Since type is now a column, we could now simply specify color=type to create one line per greenhouse gas type. As an added bonus, the summarize(value=sum(value)) now automatically summarizes the values for each gas. Often, if you have one operation that you need to apply to multiple columns, it can be a good idea to pivot them into rows first (although for advanced R users there is also the mutate(across()) option - see the documentation for more info).
As defined in the excellent book R for Data Science (written by the main creator of tidyverse), a dataset is tidy if (1) each variable is a column, (2) each row an observation, and (3) each value is a cell. In other words, rows should constitute the units of measurement, and columns should be distinct variables that are measured on each unit.
In general it is a good idea to preprocess your data to ensure it is ‘tidy’: if you have many columns that actually represent a repeated measurement (e.g. answers to the same survey question over time, or counts of different words), it is probably a good idea to pivot these into rows.
That said, it should be noted that what is a distinct measurement can depend on the data question one is trying to answer: to compute the difference between anomalies in the southern and northern hemisphere, we conceive them as distinct measurements on the same unit (year). In other words, years are the units of measurement and so we should make sure each row is exactly one year. If we want to plot distinct lines for each region, however, the anomaly is the unique measurement and the year - region combination becomes the unit of observation, so we now need to pivot back to having each row be a year - region combination.
The beauty is that the pivot commands make it quite easy to switch between wide and long, and it can actually happen that the easiest way to achieve a specific goal is to first pivot to wide, do some calculations over the columns, and then pivot back to long and summarize or plot over the rows. Practicality beats purity, as our competitors in the Python world say.
Exercise: Change in emissions per capita
Using the co2 dataset, compute the change in CO2 emissions per capita between 1990 and 2020 for each country.
filter()the data to keep only the correct rows.select()only the columns you actually need: the new row identifiers, the new columns, and the new values.pivot_wider()to spread the two years into separate columns.mutate()to compute thechangebetween the two years.
This should result in a dataset that looks like this:
| country | y2020 | y1990 | change |
|---|---|---|---|
| Afghanistan | 0.28 | 0.17 | 0.11 |
| Albania | 1.69 | 1.68 | 0.0100 |
| … | … | … | … |
Build the pipeline up one step at a time: uncomment and complete a line, run the code to see what you get, and only then move on to the next line.
- For the
filter, you need to select only the years that you need to compare - For the
select, you should keep the country (new rows), year (new columns) and co2_per_capita. - For pivot_wider, give the name of the column that forms the new columns (the years) and the new values (the CO2 per capita). Don’t quote the columns.
- For the mutate, have a look at how the columns were named after the pivot, and then use
mutate(change = ... - ...)to compute the difference.
Now that we have the change per country, let’s visualize it:
- This plot type is called a ‘dumbbell’ plot. Why? And how are the two geoms used to construct the plot? What do you think the
xendaesthetic is for? - What do you think the
left_joinabove does? - Why do we need to use both the new
change_in_emissionsand the olderco2data sets? - The 1990 and 2020 data are in separate columns, i.e. a wide format. We earlier said that ggplot generally needs a long format. Why is the wide format correct here?
- Substantively, why have some countries increased their emissions, while other decreased? Are these the countries you would expect?
- Is India or the US “doing better” on climate? What can we base this on?
7.2 Joining multiple data sets using *_join
The plot above used left_join to combine two data sets: the change_in_emissions dataset containing the 1990 and 2020 CO2 per capita, and the co2 dataset which contained the total CO2 per country.
Combining or joining data sets is a crucial operation in many data science pipelines. For example, you can join multiple survey waves; information about participants with survey questions, metadata about measurements with the actual measurements; or two sources of data about the same countries.
Returning to the example of climate change, let’s see whether rich or poor countries are the main source of pollution.
First, let’s get data about the World Bank country income classifications:
As you can see, this classifies each country into an income_group such as Low-income for each year from 1987.
Let’s combine this with the CO2 data and make a bar plot of the CO2 emissions per income group in 2020:
So, looking at all historical CO2 emissions, the rich countries emitted the most CO2 of all income groups by far. How does that change if you look at 2020 emissions rather than historical emissions?
Joining: key columns
If you look closely, right above the graph you can see a warning message printed in red: Joining with `by = join_by(country, year)`. This message is key to understanding how a join works: One or more columns are identified as the key or index columns (in this case country and year). For each unique key value, the rows from both data sets that share that value are combined. This results in a dataset with the key columns and all other columns from both data sets. In this case, it creates a data set with CO2 emissions and income classifications for all countries and years.
If you don’t specify the key columns, R will automatically join on the columns with the same name in both data sets, giving an error message if no shared columns exist. You can also explicitly specify the columns:
Since this excluded the year column from the join, the resulting dataset actually has two year columns: year.x and year.y.
You can also specify two key columns with different names, for example if country is named ‘state’ in the second dataset:
Note how this also used the suffix= argument to specify how the year column should be renamed.
Although this shows that it is possible to make the join command relatively complex, my recommendation is to not actually do this. Instead, I would always use select and/or rename to ensure both data frames have identical names for the key columns, and non-identical names for any other columns. This way, it’s easier to understand what is going on: In the join_by(left_column == right_column) syntax, you have to look back to see which data set is left and right, making it hard to parse. I would still advise listing the key columns with a simple join_by() argument to make it clear what you are joining on (and to silence the warning message).
So, to obtain the combined 2020 data I would first preprocess both data sets, and then do a simple join:
Which rows are joined? inner_join, left_join, right_join and full_join
The income classification data above was given from 1987, while the co2 data starts in 1850. What does that mean for the resulting data set?
(Note how this uses summarize without group_by to compute overall summary values, adds the dataset name with add_column, and then adds the three data sets together with bind_rows)
As you can see, the combined data set only has rows from 1987 to 2024: inner_join keeps only rows that occur in both data sets. Try replacing it with left_join, right_join and full_join. What changes?
To better understand what’s going on, let’s create an example data set with CO2 and income data for different countries:
So, in this (toy) example we use CO2 data for China, India, Russia and the US; but income data for Russia, the US and UK.
We’ve seen what happens with an inner_join: only the matching rows are kept, in this case Russia and US. What do you think will happen with a left_join?
Does this make sense to you? Why does the result include missing values (NAs)? What happens if you replace it with right_join or full_join?
This example hopefully makes the behaviour of the different joins fully clear: * inner_join keeps only rows occurring in both data sets * left_join keeps all rows in the first (left hand) data set, inserting NA where rows are missing in the other set * right_join keeps all rows in the second (right hand) data set, inserting NA where rows are missing in the other set * full_join keeps all rows from both data sets, inserting NA where rows are missing in either set.
The different join types depend on the use case. In my experience only inner_join and left_join are used in most cases:
- Use
inner_joinwhen your analysis is only useful if the data is complete, i.e. both datasets are equally important to your analysis - Use
left_jointo add information from a secondary data set to the more important data, accepting that there might be missing values in the columns from the secondary data.
Since it feels more natural to list the primary data set first, I tend to use left rather than right joins. The only exception is if you want to include the join in a longer pipeline and the earlier processing steps are used for the secondary data set – but even then I would probably just split the pipeline in two in most cases. full_join is used even less frequently, as there are very few cases where your analysis can work with missing data from both data sets.
One final tip: when running joins in R studio, keep an eye on the number of rows in the original and combined data sets in the environment pane. If the combined data has a different row count than expected, this is a good indicator that data is missing or coded differently: perhaps both data sets have a ‘country’ column, but the first data set writes United States while the second uses USA. In that case, an inner_join would silently drop the US from the result, leading to a lower row count than expected.
Emissions over time
Let’s see how emissions by income group changed over time:
(Note the use of geom_area to create a stacked area chart, which is useful for showing how both a total value and its composition change over time)
The general trend we can see in the graph makes sense: overall CO2 emissions increase, especially driven by increased emissions of developing countries. However, at least two things are more puzzling: Why would low-income countries actually emit less over time? And what can explain the strange jump in upper-middle income country emissions in 2010?
To answer the latter question, you can have a look at the income data for China around that time:
How does that explain the shift in 2010? And is there a similar explanation for the drop in emissions by low-income countries?
One to many joins
To fix the problem that showed up in the example above, we could ‘freeze’ the income classifications by taking only the most recent version:
Now, we can join it to the CO2 data over time. To test, let’s have a look at the recent values for China:
As you can see, the income group is now fixed for each country. We call this type of join a one-to-many join. Technically, the data sets are joined by country; and since co2 has multiple rows for each country (one per year) while income_latest only has a single row, the data from this row is repeated for each year in the co2 data.
Now, we can recreate the graph above with income data fixed:
Many to many joins
A final category of joins is the many-to-many join.
For example, if we would join the income and co2 data by country alone, it will create rows for each possible combination of income year and co2 year:
We selected only two rows for each dataset (China in 2009 and 2010). The resulting dataset, however, has four rows: it creates a row for each combination of co2_year and income_year, copying the corresponding co2 or income_group value in those rows.
Note that in data science, there are very few use cases for many-to-many joins, as it generally creates row pairs that substantively do not make a lot of sense: why would you want to combine 2009 income group data with 2010 emissions data? More commonly, many to many joins happen by mistake: either because the join columns are not correctly specified (as is arguably the case in the example above), or because a dataset had unexpected duplicate key values. For this reason, R outputs a warning: Detected an unexpected many-to-many relationship, which you can suppress by explicitly setting relationship = "many-to-many" in the join to indicate that the many to many join is intentional.
Semi and anti-join
As a final addition, there are two more joins that are used to filter data rather than combine columns: semi_join(a, b, join_by(columns)) filters a to keep only rows where corresponding rows existing in b; while anti_join(a, b, join_by(columns)) keeps only the rows in a that have no corresponding row in b.
The most frequent use case for this is to explore and debug problems. Suppose we would have picked 2020 as a fixed year for the income groups. We can then use anti_join to check if we actually have income data for all countries that we have co2 data for:
As you can see, this lists 12 countries for which we don’t have income data. 11 of these are sub-state territories like Anguilla or micro-states like the Vatican, but Venezuela is harder to understand. It turns out that the World Bank suspended the classification of Venezuela between 2020 and 2024 before re-classifying it as a lower middle income country. Such use of anti_join can be quite useful to prevent mistakes or understand strange outcomes, as omitting a large emitter like Venezuela because we picked 2020 as the classification year is probably not what we intended!
For understanding the behaviour of both joining and pivoting, it makes sense to think of a data frame as consisting of key columns and value columns. Key columns (such as country and year) identify cases, while value columns (such as co2) represent measurements or values of these cases.
This gives us better terms for understanding the preprocessing needed for pivoting and joining:
- Before doing
pivot_wider, it is important toselectonly key columns and the single value column that is to be pivoted. Other value columns will be mistakenly seen as key columns and create a row for each value! - Before joining, it is important that the key columns match in both datasets, and that only the key columns share the same column name. It’s easier to use
renameand/orselectto ensure that key columns are named the same and value columns are unique, than using thejoin_by(a==b)syntax and/or dealing with suffixed column names later.
Similarly, when doing pivot_longer, you have to specify the value columns that are to be pivoted (pivot_longer(value_1:value_n)); or alternatively you can specify the key column(s) with a negative selection (pivot_longer(-key_1:-key_n)).
Exercise: Are CO2 emissions and temperature correlated over time?
Now that we’ve covered both pivoting and joining, let’s combine them to answer a substantive question: is there a relation between rising global temperatures and cumulative CO2 emissions over time?
filter()the temperature data to keep only theWorldregion.group_by()year andsummarize()the co2 data to get the total cumulative CO2 emitted (globally) per year.inner_join()the two results together.- Use
cor.test()to test the correlation betweentemp_anomalyandco2.
- For
world_temp, usefilter(region == "World")to keep only the global average, not the two hemispheres. - For
world_co2, usesum(cumulative_co2, na.rm = TRUE)– note that this iscumulative_co2, notco2! We want the total emitted since 1750, not just that year’s emissions. - For
temp_and_co2, useinner_join(world_temp, world_co2). Since these two data sets now only share ayearcolumn, R will automatically join on that.
The correlation test above suggests a strong relation between temperature and cumulative CO2 emissions. But two variables that both tend to increase over time will often be strongly correlated even if they have nothing to do with each other. Let’s put both series on the same plot, rescaled to a common index, to visualize the relation:
(Note the use of pivot_longer to get both series into one column so they can share a single color aesthetic, and mutate within each group to rescale both series to the same starting index, so they’re comparable despite having very different units and magnitudes)
Quiz questions
Section 7.1 quiz: pivoting between wide and long data
pivot_wider or pivot_longer?
You have panel survey data on how much respondents trust parliament (0-10), measured in two waves:
| respondent | wave | trust |
|---|---|---|
| 1 | 2018 | 6 |
| 1 | 2022 | 4 |
| 2 | 2018 | 7 |
| 2 | 2022 | 7 |
You want to compute how much each respondent’s trust changed between the waves, so you need one row per respondent, with a separate column for each wave. Which call gives you that?
-
pivot_wider(panel, names_from=wave, values_from=trust) -
pivot_wider(panel, names_from=trust, values_from=wave) -
pivot_wider(panel, names_from=respondent, values_from=trust) -
pivot_longer(panel, names_from=wave, values_from=trust)
This data frame trust gives the average trust in parliament per country in two survey rounds, plus the number of respondents n in each round:
| country | wave | trust | n |
|---|---|---|---|
| Netherlands | 2018 | 5.6 | 1673 |
| Netherlands | 2022 | 5.1 | 1435 |
| Hungary | 2018 | 4.3 | 1661 |
| Hungary | 2022 | 3.9 | 2118 |
You want a data frame with one row per country, and the trust scores for 2018 and 2022 in separate columns. What do you need to do before running pivot_wider(names_from=wave, values_from=trust)?
-
Drop the
ncolumn, e.g. withselect(country, wave, trust). -
Nothing: you can immediately run
pivot_wider -
Group the data by country with
group_by(country), sopivot_widerknows it should create one row per country. -
Sort the data with
arrange(country), so that the rows for the same country are next to each other.
You have emissions data with one column per greenhouse gas:
| country | year | co2 | methane | nitrous_oxide |
|---|---|---|---|---|
| Brazil | 2019 | 477 | 529 | 175 |
| Brazil | 2020 | 448 | 536 | 184 |
| … | … | … | … | … |
You want to make a line plot with one colored line per gas, using aes(x=year, y=value, color=gas). What do you need to do first?
-
pivot_longer(co2:nitrous_oxide, names_to=“gas”), so the gas names become values in a newgascolumn. -
pivot_wider(names_from=gas, values_from=value), so each gas gets its own column. -
group_by(co2, methane, nitrous_oxide), so ggplot knows these are three groups. -
Nothing: ggplot automatically draws one line per numeric column when you use
color=gas.
pivot_longer
Given this data frame hemispheres with temperature anomalies:
| year | north | south |
|---|---|---|
| 2020 | 1.26 | 0.62 |
| 2021 | 1.09 | 0.55 |
A student runs pivot_longer(hemispheres, -year, names_to=region, values_to=anomaly) and gets the error object ‘region’ not found. What is the problem?
-
The new column names
regionandanomalydon’t exist yet, so they need to be quoted:names_to=“region”. -
-yearis not allowed: you have to list the columns to pivot, e.g.north:south. -
It should be
names_fromandvalues_frominstead ofnames_toandvalues_to. -
The values column must always be called
value; you can’t choose a different name.
Which of these statements about long and wide data is false?
- For every data set there is one correct, tidy shape, and once your data has that shape you should never need to pivot it again.
- To calculate the difference between two measurements of the same case (like north minus south in the same year), it is easiest to have them in separate columns.
- To plot several measurements as differently colored lines, ggplot needs them in long format, with a column that says which measurement each row is.
-
You can undo a
pivot_widerwithpivot_longer, and the other way around.
Section 7.2 quiz: joining multiple data sets
You have survey data survey with one row per respondent:
| respondent | country | trust |
|---|---|---|
| 1 | Netherlands | 6 |
| 2 | Belgium | 4 |
| 3 | Netherlands | 7 |
| 4 | Luxembourg | 5 |
And country-level data countries with GDP per capita (in thousands of dollars):
| country | gdp_pc |
|---|---|
| Netherlands | 57 |
| Belgium | 50 |
| Germany | 48 |
How many rows does inner_join(survey, countries, join_by(country)) have?
- 3
- 2
- 4
- 5
Using the same survey and countries data as in Q1, you want to add GDP information to your respondents. Your analysis is about the respondents, so you want to keep all respondents, even the one from Luxembourg for which you have no GDP data. Which call do you use?
-
left_join(survey, countries, join_by(country)) -
inner_join(survey, countries, join_by(country)) -
right_join(survey, countries, join_by(country)) -
full_join(survey, countries, join_by(country))
You join CO2 data with World Bank income groups, both filtered to 2020:
| country | year | co2 |
|---|---|---|
| India | 2020 | 2423 |
| Kenya | 2020 | 20 |
| country | year | income_group |
|---|---|---|
| India | 2020 | Lower-middle-income |
| Kenya | 2020 | Lower-middle-income |
The result of inner_join(co2_2020, income_2020, join_by(country)) has columns country, year.x, co2, year.y and income_group. Why are there two year columns?
-
Because
yearexists in both data sets but is not one of the key columns, so both versions are kept and given a suffix. - Because the year values in the two data sets are different, so dplyr can’t merge them into one column.
- Because this is a many-to-many join, which always duplicates the columns.
-
Because
inner_joinkeeps all columns from both data sets, whileleft_joinwould only keep the columns of the left data set.
You have two data frames from a panel survey, both with two waves for the same respondent:
| respondent | wave | trust |
|---|---|---|
| 1 | 1 | 6 |
| 1 | 2 | 4 |
| respondent | wave | income |
|---|---|---|
| 1 | 1 | 2500 |
| 1 | 2 | 2700 |
You run inner_join(trust, income, join_by(respondent)). What happens?
-
You get 4 rows, combining each trust wave with each income wave, and a warning about a many-to-many relationship; you should use
join_by(respondent, wave). - You get 2 rows, one per wave, because dplyr sees that the wave values match.
- You get 1 row for respondent 1, with the trust and income values of both waves combined.
- You get an error, because dplyr refuses to join data sets in which a key value occurs more than once.
After joining your CO2 and income data, the United States is missing from the result. You suspect the country is spelled differently in the two data sets:
| country | co2 |
|---|---|
| Netherlands | 137 |
| United States | 4690 |
| country | income_group |
|---|---|
| Netherlands | High-income |
| USA | High-income |
What does anti_join(co2_2020, income_2020, join_by(country)) return?
-
The
United Statesrow fromco2_2020. -
The
USArow fromincome_2020. -
The
Netherlandsrow fromco2_2020. -
Both the
United Statesand theUSArows, withNAfor the missing columns.