dplyr Используйте pivot_longer и pivot_wider для подмножества переменных - PullRequest
3 голосов
/ 28 марта 2020

Есть ли способ использовать pivot_longer и pivot_wider для подмножества переменной? Вот пример. Сначала я создам фрейм данных с желаемой стартовой структурой.

library(tidyverse)

# Assume this as starting df
arrests <- USArrests %>% 
  as_tibble(rownames = "State") %>% 
  pivot_longer(-State, names_to = "Crime", values_to = "Value") %>% 
  group_by(State) %>% 
  mutate(Total = sum(Value)) %>% 
  ungroup()

arrests
# A tibble: 200 x 4
   State   Crime    Value Total
   <chr>   <chr>    <dbl> <dbl>
 1 Alabama Murder    13.2  328.
 2 Alabama Assault  236    328.
 3 Alabama UrbanPop  58    328.
 4 Alabama Rape      21.2  328.
 5 Alaska  Murder    10    366.
 6 Alaska  Assault  263    366.
 7 Alaska  UrbanPop  48    366.
 8 Alaska  Rape      44.5  366.
 9 Arizona Murder     8.1  413.
10 Arizona Assault  294    413.
# ... with 190 more rows

Итак, мы используем фрейм данных arrest. Теперь я хотел бы сложить «Всего» в «Преступление», чтобы «Итого» было значением в Преступлении, точно так же как «Убийство».

Я также хотел бы сделать обратное. После сворачивания «Всего» в «Преступление» я хочу использовать pivot_wider для «Преступления», но только для значений, где Crime == "Total".

Возможны ли эти действия?

Ответы [ 2 ]

3 голосов
/ 28 марта 2020

Один вариант - add_row. После разделения группы на «State», l oop поверх list с map, добавьте строку (add_row из tibble) с первым значением столбца «Total» и удалите «Total». 'column

library(dplyr)
library(purrr)
library(tibble)
arrests2 <- arrests %>%
         group_split(State) %>%
         map_dfr(~ .x %>% 
               add_row(State = .$State[1], Crime = 'Total',
                        Value = .$Total[1]) %>%
                select(-Total))
arrests2
# A tibble: 250 x 3
#  State   Crime    Value
# * <chr>   <chr>    <dbl>
# 1 Alabama Murder    13.2
# 2 Alabama Assault  236  
# 3 Alabama UrbanPop  58  
# 4 Alabama Rape      21.2
# 5 Alabama Total    328. 
# 6 Alaska  Murder    10  
# 7 Alaska  Assault  263  
# 8 Alaska  UrbanPop  48  
# 9 Alaska  Rape      44.5
#10 Alaska  Total    366. 
# … with 240 more rows

Или другой вариант - summarise со значением' Total ', а затем выполнить bind_rows

arrests %>% 
   group_by(State) %>% 
   summarise(Crime = 'Total', Value = first(Total)) %>% 
   bind_rows(arrests %>% select(-Total), .)  %>% 
   arrange(State)

или с использованием pivot_longer

library(tidyr)
arrests %>%
    pivot_longer(cols = Value:Total) %>% 
    mutate(Crime = replace(Crime, name == 'Total', 'Total')) %>% 
    select(-name) %>%
    distinct()
# A tibble: 250 x 3
#   State   Crime    value
#   <chr>   <chr>    <dbl>
# 1 Alabama Murder    13.2
# 2 Alabama Total    328. 
# 3 Alabama Assault  236  
# 4 Alabama UrbanPop  58  
# 5 Alabama Rape      21.2
# 6 Alaska  Murder    10  
# 7 Alaska  Total    366. 
# 8 Alaska  Assault  263  
# 9 Alaska  UrbanPop  48  
#10 Alaska  Rape      44.5
# … with 240 more rows

Если нам нужно сделать обратное, то сгруппировать по «State», создать столбец «Total», извлекая «Value», которое соответствует «Crime» как «Total», и filter из строки, где преступление является «полным»

arrests2 %>%
    group_by(State) %>% 
    mutate(Total = Value[Crime == 'Total'])  %>%
    filter(Crime != 'Total')
# A tibble: 200 x 4
# Groups:   State [50]
#   State   Crime    Value Total
#   <chr>   <chr>    <dbl> <dbl>
# 1 Alabama Murder    13.2  328.
# 2 Alabama Assault  236    328.
# 3 Alabama UrbanPop  58    328.
# 4 Alabama Rape      21.2  328.
# 5 Alaska  Murder    10    366.
# 6 Alaska  Assault  263    366.
# 7 Alaska  UrbanPop  48    366.
# 8 Alaska  Rape      44.5  366.
# 9 Arizona Murder     8.1  413.
#10 Arizona Assault  294    413.
# … with 190 more rows
2 голосов
/ 28 марта 2020

1) janitor Используйте adorn_totals из пакета janitor, игнорируя столбец Total. Обратите внимание, что в разделе group_by эта точка относится ко всему набору данных, а не только к этой группе, если только мы не ссылаемся на нее в пределах do, поэтому мы используем do.

library(janitor)

res1 <- arrests %>%
  select(-Total) %>%
  group_by(State) %>%
  do(adorn_totals(select(., -State), "row")) %>%
  ungroup
res1

давая:

# A tibble: 250 x 3
   State   Crime    Value
   <chr>   <chr>    <dbl>
 1 Alabama Murder    13.2
 2 Alabama Assault  236  
 3 Alabama UrbanPop  58  
 4 Alabama Rape      21.2
 5 Alabama Total    328. 
 6 Alaska  Murder    10  
 7 Alaska  Assault  263  
 8 Alaska  UrbanPop  48  
 9 Alaska  Rape      44.5
10 Alaska  Total    366. 
# ... with 240 more rows

Мы можем удалить строки Total и добавить столбец

res1 %>% {
  left <- filter(., Crime != "Total")
  right <- filter(., Crime == "Total") %>% select(State, Total = Value)
  left_join(left, right, by = "State")
}

2) reshape2 Пакет reshape2 является предшественником функций pivot_ * , Он имеет встроенную функциональность полей, которая, по-видимому, не была продолжена в последующих итерациях в распространение / сборка и pivot_ *. Это также работает, если мы заменим оператор library на library(data.table).

library(reshape2)

res2 <- dcast(arrests, State + Crime ~ "Value", fun.aggregate = sum, 
  value.var = "Value", margins = "Crime")
res2

, давая:

             State    Crime Value
1          Alabama  Assault 236.0
2          Alabama   Murder  13.2
3          Alabama     Rape  21.2
4          Alabama UrbanPop  58.0
5          Alabama    (all) 328.4
6           Alaska  Assault 263.0
7           Alaska   Murder  10.0
8           Alaska     Rape  44.5
9           Alaska UrbanPop  48.0
10          Alaska    (all) 365.5
...etc...

Чтобы создать столбец Total и удалить строки, создайте фактор который идентифицирует каждую строку как строку значения или итога, а затем выводит результат в широкие формы, заполняя в NA с na.locf.

library(reshape2)
library(zoo)

fac <- factor(res$Crime == '(all)', labels = c("Value", "Total"))
dc <- dcast(res2, State + Crime ~ fac, value.var = "Value")
subset(na.locf(dc, fromLast = TRUE), Crime != '(all)')

или

left <- subset(res2, Crime != "(all)")
right <- subset(res2, Crime == "(all)", c(State, Value))
names(right) <- c("State", "Total")
merge(left, right, by = "State")

3) sqldf Чтобы использовать SQL, добавьте столбец уровня, который равен 0 для подробных записей и 1 для итоговых записей, а затем объедините детали и итоги и выполните сортировку.

library(sqldf)
res3 <- sqldf("select State, Crime, Value from (
  select 0 as level, State, Crime, Value from arrests
  union
  select 1 as level, State, 'Total' as Crime, sum(Value) as Total from arrests
  group by State)
  order by State, level")

Чтобы удалить итоговые строки и вставить a Итоговый столбец

sqldf("select State, Crime, Value, Total
  from res3 a
  left join (
     select State, sum(Value) as Total 
       from res3 
       where Crime != 'Total' 
       group by State) using (State)
  where Crime != 'Total'")

4) База R Это прямо в базе R с использованием xtabs и addmargins.

Total <- sum
tab <- addmargins(xtabs(Value ~ State + Crime, arrests), 2, FUN = Total)
DF <- as.data.frame(tab, responseName = "Value")
res3 <- DF[order(DF$State, DF$Crime == "Total"), ]

и модификацией ( 2) мы можем использовать следующее для удаления строк Total и добавления столбца Total:

left <- subset(res3, Crime != "Total")
right <- subset(res3, Crime == "Total", c(State, Value))
names(right) <- c("State", "Total")
merge(left, right, by = "State")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...