Reputation: 19
df <- data.frame(animal = c("dog", "dog", "cat", "dog", "cat", "cat"),
hunger = c(0, 1, 1, 0, 1,1))
I have a dataframe like the one above with two columns, one containing categories and the other containing binary data.
I am looking to reshape the dataframe to split the category ("animal") column up into two columns of its own with the values of "animal" column as column names and the values of the other column (hunger) as cell values, i.e.
Desired output:
df <- data.frame(dog = c(0, 1, 0),
cat = c(1, 1, 1))
How can I achieve this?
Upvotes: 17
Views: 1153
Reputation: 101034
In the case of uneven length among different categories, we can use
list2DF(
lapply(
. <- unstack(df, hunger ~ animal),
`length<-`,
max(lengths(.))
)
)
or
list2DF(
lapply(
. <- unstack(rev(df)),
`length<-`,
max(lengths(.))
)
)
and we will obtain
cat dog
1 1 0
2 1 1
3 1 0
4 0 NA
Dummy data
df <- data.frame(
animal = c("dog", "dog", "cat", "dog", "cat", "cat", "cat"),
hunger = c(0, 1, 1, 0, 1, 1, 0)
)
We can also use unstack
, e.g.,
> unstack(rev(df))
cat dog
1 1 0
2 1 1
3 1 0
or
> unstack(df, hunger ~ animal)
cat dog
1 1 0
2 1 1
3 1 0
Upvotes: 10
Reputation: 78917
Base R:
df$id <- ave(df$hunger, df$animal, FUN = seq_along)
reshape(df, idvar = "id", timevar = "animal", direction = "wide")[, -1]
hunger.dog hunger.cat
1 0 1
2 1 1
4 0 1
Upvotes: 9
Reputation: 886938
Using data.table
library(data.table)
dcast(setDT(df), rowid(animal) ~ animal)[, animal := NULL][]
-output
cat dog
1: 1 0
2: 1 1
3: 1 0
Upvotes: 7
Reputation: 56004
Using split:
data.frame(split(df$hunger, df$animal))
# cat dog
# 1 1 0
# 2 1 1
# 3 1 0
Upvotes: 9
Reputation: 7385
Throwing a tidyverse/purrr
solution into the mix:
library(tidyverse)
df <- data.frame(animal = c("dog", "dog", "cat", "dog", "cat", "cat"),
hunger = c(0, 1, 1, 0, 1,1))
df %>%
group_split(animal) %>%
map(~tibble(!!quo_name(unique(.x$animal)) := .x$hunger)) %>%
list_cbind()
#> # A tibble: 3 × 2
#> cat dog
#> <dbl> <dbl>
#> 1 1 0
#> 2 1 1
#> 3 1 0
Upvotes: 7
Reputation: 1252
A tidy framework way
library(dplyr)
library(tidyr)
df |>
pivot_wider(names_from = animal, values_from = hunger, values_fn = list) |>
unnest(cols = c("dog", "cat"))
Base R
do.call(cbind.data.frame, tapply(df$hunger, df$animal, `+`))
Upvotes: 7
Reputation: 41225
You could use pivot_wider
by first creating an id for each group to identify the duplicates and use the names_from
and values_from
like this:
library(dplyr)
library(tidyr)
df %>%
group_by(animal) %>%
mutate(id = row_number()) %>%
pivot_wider(names_from = animal, values_from = hunger) %>%
select(-id)
#> # A tibble: 3 × 2
#> dog cat
#> <dbl> <dbl>
#> 1 0 1
#> 2 1 1
#> 3 0 1
Created on 2023-03-17 with reprex v2.0.2
Upvotes: 7