Transforming a variable using the if- else if function - r

I have a data set that is that I want to calculate z scores by their year.
Example:
Year Score
1999 120
1999 132
1998 120
1997 132
2000 120
2002 132
1998 160
1997 142
....etc
What I want is:
Year Score Z-Score
1999 120 1.2
1999 132 .01
1998 120 -.6
1997 132 1.1
2000 120 -.6
2002 132 0.5
1998 160 2.1
1997 142 .01
I have used the following code:
DF$ZScore<-if (DR$Year== 1997){
((DF$Score-220)/20)
} else if ((DR$Year== 1998){
((DF$Score-222)/19)
}...
}else{
((DF$Score-219)/21)
}
This is not working and I cannot figure out why. Any help is appreciated.

I'm using the gapminder data for simplicity, and also the built in scale function. You might want to build your own function to apply depending on exactly how you want to scale it.
this is a little clukly, but beause you want per year scaling, then you could group by the year and make a nested data frame.
Then using purr, you could go into each data.frame within a year, and scale the variable you want.
Then you would unnest the data again, and the variable would be scaled within each year.
library(tidyverse)
library(gapminder)
gapminder::gapminder %>%
group_by(year) %>%
nest() %>%
mutate(data = map(data,
~ mutate_at(.x, vars(lifeExp, pop),
list(scale = scale)))) %>%
unnest(data)
#> # A tibble: 1,704 x 8
#> # Groups: year [12]
#> year country continent lifeExp pop gdpPercap lifeExp_scale[,…
#> <int> <fct> <fct> <dbl> <int> <dbl> <dbl>
#> 1 1952 Afghan… Asia 28.8 8.43e6 779. -1.66
#> 2 1952 Albania Europe 55.2 1.28e6 1601. 0.505
#> 3 1952 Algeria Africa 43.1 9.28e6 2449. -0.489
#> 4 1952 Angola Africa 30.0 4.23e6 3521. -1.56
#> 5 1952 Argent… Americas 62.5 1.79e7 5911. 1.10
#> 6 1952 Austra… Oceania 69.1 8.69e6 10040. 1.64
#> 7 1952 Austria Europe 66.8 6.93e6 6137. 1.45
#> 8 1952 Bahrain Asia 50.9 1.20e5 9867. 0.154
#> 9 1952 Bangla… Asia 37.5 4.69e7 684. -0.947
#> 10 1952 Belgium Europe 68 8.73e6 8343. 1.55
#> # … with 1,694 more rows, and 1 more variable: pop_scale[,1] <dbl>
Created on 2020-06-25 by the reprex package (v0.3.0)

Related

save the list objects created in forloop with different names in R

I would like to save all the list objects which are created by for loop as different datasets in the environment with their proper name for example gapminder_Asia, gapminder_Europe,..etc. Many thanks in advance.
library(gapminder)
cont <- unique(gapminder$continent)
df <- NULL
for(i in 1:(length(cont))) {
temp <- gapminder[gapminder$continent == cont[i], ]
colnames(temp) <- paste0(paste(cont[i]))
df[[i]] <- temp
}
df
Expected Answer,
> unique(gapminder$continent)
[1] Asia Europe Africa Americas Oceania
head(gapminder_Asia)
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
4 Afghanistan Asia 1967 34.0 11537966 836.
5 Afghanistan Asia 1972 36.1 13079460 740.
Personally I would prefer to keep the dataset inside a list using e.g. split but if your desired result is to have different named objects then you could do so via assign:
library(gapminder)
df <- split(gapminder, gapminder$continent)
for(i in names(df)) {
assign(paste("gapminder", i, sep = "_"), df[[i]])
}
gapminder_Africa
#> # A tibble: 624 × 6
#> country continent year lifeExp pop gdpPercap
#> <fct> <fct> <int> <dbl> <int> <dbl>
#> 1 Algeria Africa 1952 43.1 9279525 2449.
#> 2 Algeria Africa 1957 45.7 10270856 3014.
#> 3 Algeria Africa 1962 48.3 11000948 2551.
#> 4 Algeria Africa 1967 51.4 12760499 3247.
#> 5 Algeria Africa 1972 54.5 14760787 4183.
#> 6 Algeria Africa 1977 58.0 17152804 4910.
#> 7 Algeria Africa 1982 61.4 20033753 5745.
#> 8 Algeria Africa 1987 65.8 23254956 5681.
#> 9 Algeria Africa 1992 67.7 26298373 5023.
#> 10 Algeria Africa 1997 69.2 29072015 4797.
#> # … with 614 more rows
Created on 2021-10-16 by the reprex package (v2.0.1)

Compare Life Expectancy from an initial year 1952, and compare that expectancy to all further years, for all countries in R using Dplyr

In my R class, we are currently learning how to manipulate Tibbles. I have a homework problem where I need to grab the life expectancy from 1952 for a country and compare it to all its other expectancies for however many years of data the tibble has. For all countries within the table, in one line using pipes.
Background: this table is called gap
I have used the line:
gap %>% group_by(year, lifeExp) %>% filter(year == 1952)
To filter out the lifeExp for all countries during 1952, but from there I have no idea how to pipe back into the table and compare those initial values to the other specific country values. I know what all the basic dplyr functions do, just having trouble seeing the bigger picture with all the pipes.
If this wasn't enough to understand, I will edit! Thank you for any kind of support!
You can solve it with the help of mutate and match.
library(dplyr)
gapminder::gapminder %>%
group_by(country) %>%
mutate(difference = lifeExp - lifeExp[match(1952, year)]) %>%
ungroup -> gap
gap
# country continent year lifeExp pop gdpPercap difference
# <fct> <fct> <int> <dbl> <int> <dbl> <dbl>
# 1 Afghanistan Asia 1952 28.8 8425333 779. 0
# 2 Afghanistan Asia 1957 30.3 9240934 821. 1.53
# 3 Afghanistan Asia 1962 32.0 10267083 853. 3.20
# 4 Afghanistan Asia 1967 34.0 11537966 836. 5.22
# 5 Afghanistan Asia 1972 36.1 13079460 740. 7.29
# 6 Afghanistan Asia 1977 38.4 14880372 786. 9.64
# 7 Afghanistan Asia 1982 39.9 12881816 978. 11.1
# 8 Afghanistan Asia 1987 40.8 13867957 852. 12.0
# 9 Afghanistan Asia 1992 41.7 16317921 649. 12.9
#10 Afghanistan Asia 1997 41.8 22227415 635. 13.0
# … with 1,694 more rows

Using geom_boxplot yields different result than base boxplot()

I'm using the gapminder dataset to practice some basic data analysis on the data frame.
I want to create a subset of this data with only Argentina and New Zealand, in order to compare their values.
install.packages("gapminder")
library(gapminder)
data("gapminder")
> gapminder
# A tibble: 1,704 x 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
4 Afghanistan Asia 1967 34.0 11537966 836.
5 Afghanistan Asia 1972 36.1 13079460 740.
6 Afghanistan Asia 1977 38.4 14880372 786.
7 Afghanistan Asia 1982 39.9 12881816 978.
8 Afghanistan Asia 1987 40.8 13867957 852.
9 Afghanistan Asia 1992 41.7 16317921 649.
10 Afghanistan Asia 1997 41.8 22227415 635.
# ... with 1,694 more rows
I'm subsetting the information I want like so :
df <- subset(gapminder, country =="Argentina" | country == "New Zealand")
> df
# A tibble: 24 x 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Argentina Americas 1952 62.5 17876956 5911.
2 Argentina Americas 1957 64.4 19610538 6857.
3 Argentina Americas 1962 65.1 21283783 7133.
4 Argentina Americas 1967 65.6 22934225 8053.
5 Argentina Americas 1972 67.1 24779799 9443.
6 Argentina Americas 1977 68.5 26983828 10079.
7 Argentina Americas 1982 69.9 29341374 8998.
8 Argentina Americas 1987 70.8 31620918 9140.
9 Argentina Americas 1992 71.9 33958947 9308.
10 Argentina Americas 1997 73.3 36203463 10967.
# ... with 14 more rows
This works great as you can see (or that's what it seems)
Now I would like to create a simple boxplot to quickly analyze some values, but when I plot this with boxplot() and geom_boxplot I get two different results:
boxplot(lifeExp ~ country)
This is what I want, but the x axis is also taking into account all the other countries I did not select. Clearly their data is null but it makes the plot unreadable.
Instead if I use the same data and everything on ggplot, then it works perfectly:
ggplot(data = df, mapping = aes(x=country, y=lifeExp)) + geom_boxplot()
Is there something wrong I'm doing while defining the subset? Using boxplot() gives me the impression that the subset is keeping everything but putting the values for the things I don't want to NULL.
Start with the code posted in the question.
library(gapminder)
data("gapminder")
df <- subset(gapminder, country =="Argentina" | country == "New Zealand")
boxplot(lifeExp ~ country, df)
The plot shows space for all countries because country is a factor and subsetting keeps its original levels. With str, it can be seen what df is:
str(df)
#tibble [24 × 6] (S3: tbl_df/tbl/data.frame)
# $ country : Factor w/ 142 levels "Afghanistan",..: 5 5 5 5 5 5 5 5 5 5 ...
# $ continent: Factor w/ 5 levels "Africa","Americas",..: 2 2 2 2 2 2 2 2 2 2 ...
# $ year : int [1:24] 1952 1957 1962 1967 1972 1977 1982 1987 1992 1997 ...
# $ lifeExp : num [1:24] 62.5 64.4 65.1 65.6 67.1 ...
# $ pop : int [1:24] 17876956 19610538 21283783 22934225 24779799 26983828 29341374 31620918 33958947 36203463 ...
# $ gdpPercap: num [1:24] 5911 6857 7133 8053 9443 ...
The factor country has 142 levels.
The solution is to drop the extra levels.
df2 <- df
df2$country <- droplevels(df2$country)
boxplot(lifeExp ~ country, df2)

How to get first values from diferent columns at same time?

I'm trying to get firsts values from diferent columns to make a data frame, but I I get stranded at one point and don't know how to solve it. Imagine you're using gapminder and want to get three higer gdppercap values for each region/year. How would you do it with dplyr?
Thanks.
I'm inferring that region is continent; if it were country, then this filter would return all rows, since each country/year combination occurs only once (so "top 3" means nothing special).
library(dplyr)
gapminder::gapminder %>%
group_by(continent, year) %>%
slice_max(desc(gdpPercap), n = 3) %>%
ungroup()
# # A tibble: 168 x 6
# country continent year lifeExp pop gdpPercap
# <fct> <fct> <int> <dbl> <int> <dbl>
# 1 Lesotho Africa 1952 42.1 748747 299.
# 2 Guinea-Bissau Africa 1952 32.5 580653 300.
# 3 Eritrea Africa 1952 35.9 1438760 329.
# 4 Lesotho Africa 1957 45.0 813338 336.
# 5 Eritrea Africa 1957 38.0 1542611 344.
# 6 Ethiopia Africa 1957 36.7 22815614 379.
# 7 Burundi Africa 1962 42.0 2961915 355.
# 8 Eritrea Africa 1962 40.2 1666618 381.
# 9 Lesotho Africa 1962 47.7 893143 412.
# 10 Burundi Africa 1967 43.5 3330989 413.
# # ... with 158 more rows

Multiply pairs of columns using dplyr in R

I have a dataframe with crime data and associated "prices", organized by country and year (although I don't think this is important here). Here is a subset of my data:
> crime
# A tibble: 8 x 8
iso year theft robbery burglary theft_price robbery_price burglary_price
<chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 ALB 2003 3694 199 874 32.9 115 49.3
2 ALB 2004 3694 199 874 38.2 134 57.3
3 ALB 2005 3694 199 874 42.8 150 64.2
4 ALB 2006 3450 164 779 47.0 165 70.5
5 AUS 2003 722334 14634 586266 408.4 1427 612.4
6 AUS 2004 636717 14634 512551 481.3 1683 721.2
7 AUS 2005 598700 14634 468558 536.7 1877 804.5
8 AUS 2006 594111 14634 433974 564.8 1973 846.5
I want to create new columns that contain the product of each crime type with its price, so theft x theft_price = theft_prod, etc. In my actual dataset I have more crime types so I need something that is scalable to more variables than this subset contains.
I like the syntax of the dplyr package, so I to use something like this, but I cannot find the solution. I think it is not possible to reference other variables than the ones in vars(). Correct?
crime %>%
mutate_at(vars(theft, robbery, burglary),
funs(prod = . * ????))
Thanks.
Use dplyr and tidyr:
library(dplyr); library(tidyr);
df %>%
gather(crime, value, -iso, -year) %>%
separate(crime, c('crime', 'type'), sep='_', fill = 'right') %>%
replace_na(list(type = 'amount')) %>%
spread(type, value) %>%
transmute(
iso = iso, year = year,
crime = paste(crime, 'prod', sep = '_'),
prod = amount * price
) %>%
spread(crime, prod)
# iso year burglary_prod robbery_prod theft_prod
#1 ALB 2003 43088.2 22885 121532.6
#2 ALB 2004 50080.2 26666 141110.8
#3 ALB 2005 56110.8 29850 158103.2
#4 ALB 2006 54919.5 27060 162150.0
#5 AUS 2003 359029298.4 20882718 295001205.6
#6 AUS 2004 369651781.2 24629022 306451892.1
#7 AUS 2005 376954911.0 27468018 321322290.0
#8 AUS 2006 367358991.0 28872882 335553892.8
Another option without data reshaping, assuming the columns' names follow the crime_price convention:
library(tidyverse)
# find out the crimes columns
crimes = grep('^(?!.*_price$)', names(df)[-c(1,2)], perl = T, value = T)
# construct the crimes prices columns
crimes_prices = paste(crimes, 'price', sep = '_')
crimes_prod = paste(crimes, 'prod', sep = '_')
# loop through crime and crime price columns and multiply them
map2(crimes, crimes_prices, ~ df[[.x]] * df[[.y]]) %>%
set_names(crimes_prod) %>%
as_tibble() %>%
bind_cols(select(df, iso, year))
# A tibble: 8 x 5
# theft_prod robbery_prod burglary_prod iso year
# <dbl> <int> <dbl> <fct> <int>
#1 121533. 22885 43088. ALB 2003
#2 141111. 26666 50080. ALB 2004
#3 158103. 29850 56111. ALB 2005
#4 162150 27060 54920. ALB 2006
#5 295001206. 20882718 359029298. AUS 2003
#6 306451892. 24629022 369651781. AUS 2004
#7 321322290 27468018 376954911 AUS 2005
#8 335553893. 28872882 367358991 AUS 2006
Doing this kind of manipulation in the tidyverse is best done by making sure your data is tidy by reshaping it. A purrr approach is also possible but is likely reliant on the order of your columns, which might not always be reliable. Instead, you can do the following:
gather up all your measure columns
mutate a new column measure_type that indicates whether it is a count or price, and remove the _price from crime_type. Now we have separate columns for the type of crime and the metric we are using for that crime. Each row is a single iso-year-crime-metric combination.
spread the crime types back out so now we have separate count and price columns for all crimes, and then multiply with mutate.
(optional) if you want to put it back in your wide format, we just gather up count and price and our new product column, unite to combine with the crime type and spread back out.
library(tidyverse)
tbl <- read_table2(
"iso year theft robbery burglary theft_price robbery_price burglary_price
ALB 2003 3694 199 874 32.9 115 49.3
ALB 2004 3694 199 874 38.2 134 57.3
ALB 2005 3694 199 874 42.8 150 64.2
ALB 2006 3450 164 779 47.0 165 70.5
AUS 2003 722334 14634 586266 408.4 1427 612.4
AUS 2004 636717 14634 512551 481.3 1683 721.2
AUS 2005 598700 14634 468558 536.7 1877 804.5
AUS 2006 594111 14634 433974 564.8 1973 846.5"
)
tidy_tbl <- tbl %>%
gather(crime_type, measure, -iso, - year) %>%
mutate(
measure_type = if_else(str_detect(crime_type, "_price$"), "price", "count"),
crime_type = str_remove(crime_type, "_price")
) %>%
spread(measure_type, measure) %>%
mutate(product = count * price)
tidy_tbl
#> # A tibble: 24 x 6
#> iso year crime_type count price product
#> <chr> <int> <chr> <dbl> <dbl> <dbl>
#> 1 ALB 2003 burglary 874 49.3 43088.
#> 2 ALB 2003 robbery 199 115 22885
#> 3 ALB 2003 theft 3694 32.9 121533.
#> 4 ALB 2004 burglary 874 57.3 50080.
#> 5 ALB 2004 robbery 199 134 26666
#> 6 ALB 2004 theft 3694 38.2 141111.
#> 7 ALB 2005 burglary 874 64.2 56111.
#> 8 ALB 2005 robbery 199 150 29850
#> 9 ALB 2005 theft 3694 42.8 158103.
#> 10 ALB 2006 burglary 779 70.5 54920.
#> # ... with 14 more rows
tidy_tbl %>%
gather(measure_type, measure, count:product) %>%
unite("colname", crime_type, measure_type) %>%
spread(colname, measure)
#> # A tibble: 8 x 11
#> iso year burglary_count burglary_price burglary_product robbery_count
#> <chr> <int> <dbl> <dbl> <dbl> <dbl>
#> 1 ALB 2003 874 49.3 43088. 199
#> 2 ALB 2004 874 57.3 50080. 199
#> 3 ALB 2005 874 64.2 56111. 199
#> 4 ALB 2006 779 70.5 54920. 164
#> 5 AUS 2003 586266 612. 359029298. 14634
#> 6 AUS 2004 512551 721. 369651781. 14634
#> 7 AUS 2005 468558 804. 376954911 14634
#> 8 AUS 2006 433974 846. 367358991 14634
#> # ... with 5 more variables: robbery_price <dbl>, robbery_product <dbl>,
#> # theft_count <dbl>, theft_price <dbl>, theft_product <dbl>
Created on 2018-08-15 by the reprex package (v0.2.0).

Resources