stat_stud
stat_stud

Reputation: 107

How would you loop this in R?

I have this kinda simple task I'm having hard time looping.

So, lets assume I have this tibble:

library(tidyverse)
dat <- tibble(player1 = c("aa","bb","cc"), player2 = c("cc","aa","bb"))

My goal here, is to make three new columns ( for each unique "player" I have) and assign value of 1 to the column, if the player is "player1", -1 if the player is "player2" and 0 otherwise.

Previously, I have been doing it like this:

dat %>% mutate( aa = ifelse(player1 == "aa",1,ifelse(player2 == "aa",-1,0)),
          bb = ifelse(player1 == "bb",1,ifelse(player2 == "bb",-1,0)),
          cc = ifelse(player1 == "cc",1,ifelse(player2 == "cc",-1,0)))

This works, but now I have hundreds of different "players", so it would seem silly to do this manually like that. I have tried and read about loops in R, but I just can't get this one right.

Upvotes: 4

Views: 97

Answers (3)

Darren Tsai
Darren Tsai

Reputation: 35554

You can use pivot_longer() to stack those columns starting with "player" and then pivot it to wide. The advantage is that you can do recoding within pivot_wider() by the argument values_fn.

library(tidyverse)

dat %>%
  rowid_to_column("id") %>%
  pivot_longer(starts_with("player")) %>%
  pivot_wider(names_from = value, names_sort = TRUE,
              values_from = name, values_fill = 0,
              values_fn = function(x) c(1, -1)[match(x, c("player1", "player2"))])

# # A tibble: 3 x 4
#      id    aa    bb    cc
#   <int> <dbl> <dbl> <dbl>
# 1     1     1     0    -1
# 2     2    -1     1     0
# 3     3     0    -1     1

Note: Development on gather()/spread() is complete, and for new code we recommend switching to pivot_longer()/_wider(), which is easier to use, more featureful, and still under active development.

Upvotes: 2

s_baldur
s_baldur

Reputation: 33488

Using model.matrix() from base R:

dat[unique(dat$player1)] <- 
  model.matrix(~0+ player1, data = dat) - model.matrix(~0+ player2, data = dat)

dat
  player1 player2    aa    bb    cc
  <chr>   <chr>   <dbl> <dbl> <dbl>
1 aa      cc          1     0    -1
2 bb      aa         -1     1     0
3 cc      bb          0    -1     1

This assumes you have all players in both columns. Otherwise you would need to convert them to factors with the appropriate levels and replace unique with levels.

Upvotes: 5

Aur&#232;le
Aur&#232;le

Reputation: 12819

We could go from initial structure to "long"(-er) format, with one row per (game, player), recode to 1/-1, and then go wide again with the desired output:

dat %>% 
  mutate(game_id = row_number()) %>% 
  gather("role", "player", -game_id) %>% 
  mutate(role = recode(role, "player1" = 1L, "player2" = -1L)) %>% 
  spread(player, role, fill = 0L)

#> # A tibble: 3 x 4
#>   game_id    aa    bb    cc
#>     <int> <int> <int> <int>
#> 1       1     1     0    -1
#> 2       2    -1     1     0
#> 3       3     0    -1     1

Upvotes: 2

Related Questions