Як переформатувати дані від довгого до широкого формату


262

У мене виникають проблеми з перестановкою наступного кадру даних:

set.seed(45)
dat1 <- data.frame(
    name = rep(c("firstName", "secondName"), each=4),
    numbers = rep(1:4, 2),
    value = rnorm(8)
    )

dat1
       name  numbers      value
1  firstName       1  0.3407997
2  firstName       2 -0.7033403
3  firstName       3 -0.3795377
4  firstName       4 -0.7460474
5 secondName       1 -0.8981073
6 secondName       2 -0.3347941
7 secondName       3 -0.5013782
8 secondName       4 -0.1745357

Я хочу змінити її так, щоб кожна унікальна змінна "ім'я" була назвою рядка, "значення" як спостереження за цим рядком, а "числа" - як назви. На зразок цього:

     name          1          2          3         4
1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
5 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

Я переглянув meltі castкілька інших речей, але, схоже, жодна робота не справляється.



4
@Frank: це набагато краща назва. довга форма та широка форма - це стандартні вживані терміни. Інша відповідь не може бути знайдена шляхом пошуку за цими термінами.
smci

ще одне питання: як це змінити назад?
HappyLiang

Відповіді:


255

Використання reshapeфункції:

reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")

13
+1, і вам не потрібно покладатися на зовнішні пакунки, оскільки reshapeпостачається з stats. Не кажучи вже про те, що це швидше! =)
aL3xa

@indra_patil - я, швидше за все, буду використовувати пакет reshape2, як зазначено в одній з інших відповідей. Ви можете створити нове запитання, характерне для вашої справи використання, і опублікувати його, якщо не можете розібратися.
Чейз

5
reshapeє чудовим прикладом API жахливих функцій. Це дуже близько до марного.
NoBackingDown

14
Ці reshapeкоментарі та подібні імена аргументів не всі , що корисні. Однак я виявив, що надовго до широкого вам потрібно надати data =свій data.frame, idvar= змінна, яка ідентифікує ваші групи, v.names= змінні, які стануть кількома стовпцями у широкому форматі, timevar= змінна, що містить значення, які будуть додані до v.namesу широкому форматі direction = wide, та sep = "_". Досить ясно? ;)
Брайан Д

3
Я б сказав, що база R все ще виграє голос на коефіцієнт приблизно 2 до 1
vonjd

129

Новий (у 2014 році) tidyrпакет також робить це просто, з gather()/ spread()умовами для melt/ cast.

Edit: Тепер, в 2019 році, tidyr v 1,0 запустив і безліч spreadі gatherна шляху старіння, вважаючи за краще замість цього pivot_widerі pivot_longer, які ви можете знайти описані в цій відповіді . Читайте далі, якщо хочете короткий погляд на коротке життя Росії spread/gather.

library(tidyr)
spread(dat1, key = numbers, value = value)

Від github ,

tidyrце перероблення, reshape2призначене супроводжувати охайний фреймворк даних, а також співпрацювати magrittrта dplyrбудувати міцний конвеєр для аналізу даних.

Так само, як reshape2і менше, ніж змінити, tidyrчи менше reshape2. Він розроблений спеціально для впорядкування даних, а не загальної переформатування, яка reshape2робиться, або загальної агрегації, яка була перероблена. Зокрема, вбудовані методи працюють лише для фреймів даних і tidyrне забезпечують запасів чи агрегації.


5
Просто хотів додати посилання на сторінку R Cookbook, яка обговорює використання цих функцій від tidyrта reshape2. Це дає хороші приклади та пояснення.
Джейк

71

Це можна зробити за допомогою reshape()функції або з melt()/ cast()функціями в пакеті переформатування. Для другого варіанта, приклад код є

library(reshape)
cast(dat1, name ~ numbers)

Або за допомогою reshape2

library(reshape2)
dcast(dat1, name ~ numbers)

2
Можливо, варто відзначити, що просто використовувати castабо dcastне буде добре працювати, якщо у вас немає чіткого стовпця "значення". Спробуйте, dat <- data.frame(id=c(1,1,2,2),blah=c(8,4,7,6),index=c(1,2,1,2)); dcast(dat, id ~ index); cast(dat, id ~ index)і ви не отримаєте того, чого очікуєте. Вам потрібно чітко зазначити value/value.var- cast(dat, id ~ index, value="blah")і, dcast(dat, id ~ index, value.var="blah")наприклад.
thelatemail

44

Інший варіант, якщо продуктивність викликає занепокоєння - використовувати data.tableрозширення функцій reshape2'telt & dcast'

( Довідка: Ефективна перестановка за допомогою таблиць data.tables )

library(data.table)

setDT(dat1)
dcast(dat1, name ~ numbers, value.var = "value")

#          name          1          2         3         4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814

І, що стосується data.table v1.9.6, ми можемо навести декілька стовпців

## add an extra column
dat1[, value2 := value * 2]

## cast multiple value columns
dcast(dat1, name ~ numbers, value.var = c("value", "value2"))

#          name    value_1    value_2   value_3   value_4   value2_1   value2_2 value2_3  value2_4
# 1:  firstName  0.1836433 -0.8356286 1.5952808 0.3295078  0.3672866 -1.6712572 3.190562 0.6590155
# 2: secondName -0.8204684  0.4874291 0.7383247 0.5757814 -1.6409368  0.9748581 1.476649 1.1515627

5
data.tableпідхід найкращий! дуже ефективно ... Ви побачите різницю, коли nameце комбінація 30-40 стовпців !!
joel.wilson

Що робити, якщо я хотів взяти максимум?
T.Fung

@ T.Fung Я не розумію, про що ти питаєш. Можливо, найкраще відкрити нове запитання?
SymbolixAU

@SymbolixAU у запитанні оп "ім'я" та "числа" є унікальними комбінаціями. Що робити, якщо їх не було, і я хотів отримати максимальне значення для кожної комбінації після повороту? Не проблема, якщо занадто прискіпливе запитання. Просто їжа для думок. Дякую.
T.Fung

Чудова відповідь. Дякую. Для кількох стовпців, я отримав «Помилка в .subset2 (х, я, точний = точний)», і може усунути цю проблему , змушуючи використовувати data.table dcast: см stackoverflow.com/a/44271092/190791
Timothee HENRY

26

Використовуючи ваш приклад фрейму даних, ми могли б:

xtabs(value ~ name + numbers, data = dat1)

2
це добре, але результат - форматна таблиця, яка може бути не такою простою в обробці, як data.frame або data.table, в обох є багато пакетів
cloudcomputes

18

Інші два варіанти:

Базовий пакет:

df <- unstack(dat1, form = value ~ numbers)
rownames(df) <- unique(dat1$name)
df

sqldf пакет:

library(sqldf)
sqldf('SELECT name,
      MAX(CASE WHEN numbers = 1 THEN value ELSE NULL END) x1, 
      MAX(CASE WHEN numbers = 2 THEN value ELSE NULL END) x2,
      MAX(CASE WHEN numbers = 3 THEN value ELSE NULL END) x3,
      MAX(CASE WHEN numbers = 4 THEN value ELSE NULL END) x4
      FROM dat1
      GROUP BY name')

1
Замість номерів жорсткого кодування запит можна налаштувати так:ValCol <- unique(dat1$numbers);s <- sprintf("MAX(CASE WHEN numbers = %s THEN value ELSE NULL END) `%s`,", ValCol, ValCol);mquerym <- gsub('.{1}$','',paste(s, collapse = "\n"));mquery <- paste("SELECT name,", mquerym, "FROM dat1", "GROUP BY name", sep = "\n");sqldf(mquery)
M--

13

Використання базової aggregateфункції R :

aggregate(value ~ name, dat1, I)

# name           value.1  value.2  value.3  value.4
#1 firstName      0.4145  -0.4747   0.0659   -0.5024
#2 secondName    -0.8259   0.1669  -0.8962    0.1681

11

З розробленою версією tidyr ‘0.8.3.9000’, існує pivot_widerі pivot_longerяка узагальнена, щоб зробити переформатування (довге -> широке, широке -> довге, відповідно) від 1 до кількох стовпців. Використання даних ОП

-одиновий стовпчик довгий -> широкий

library(dplyr)
library(tidyr)
dat1 %>% 
    pivot_wider(names_from = numbers, values_from = value)
# A tibble: 2 x 5
#  name          `1`    `2`    `3`    `4`
#  <fct>       <dbl>  <dbl>  <dbl>  <dbl>
#1 firstName   0.341 -0.703 -0.380 -0.746
#2 secondName -0.898 -0.335 -0.501 -0.175

-> створили ще один стовпчик для показу функціональності

dat1 %>% 
    mutate(value2 = value * 2) %>% 
    pivot_wider(names_from = numbers, values_from = c("value", "value2"))
# A tibble: 2 x 9
#  name       value_1 value_2 value_3 value_4 value2_1 value2_2 value2_3 value2_4
#  <fct>        <dbl>   <dbl>   <dbl>   <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
#1 firstName    0.341  -0.703  -0.380  -0.746    0.682   -1.41    -0.759   -1.49 
#2 secondName  -0.898  -0.335  -0.501  -0.175   -1.80    -0.670   -1.00    -0.349

8

Базова reshapeфункція працює чудово:

df <- data.frame(
  year   = c(rep(2000, 12), rep(2001, 12)),
  month  = rep(1:12, 2),
  values = rnorm(24)
)
df_wide <- reshape(df, idvar="year", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Де

  • idvar це стовпець класів, який розділяє рядки
  • timevar - це стовпчик класів, який слід розширити
  • v.names - стовпець, що містить числові значення
  • direction задає широкий або довгий формат
  • необов'язковий sepаргумент - це роздільник, який використовується між timevarіменами класів та v.namesу висновку data.frame.

Якщо такої idvarнемає, створіть її перед використанням reshape()функції:

df$id   <- c(rep("year1", 12), rep("year2", 12))
df_wide <- reshape(df, idvar="id", timevar="month", v.names="values", direction="wide", sep="_")
df_wide

Просто пам’ятайте, що idvarпотрібно! timevarІ v.namesчастина легко. Вихід цієї функції є більш передбачуваним, ніж деякі інші, оскільки все чітко визначено.


7

Там дуже потужний новий пакет з геніальних вчених даних в Win-Vector (люди , які зробили vtreat, seplyrі replyr) називається cdata. Він реалізує принципи "узгоджених даних", описані в цьому документі, а також у цій публікації в блозі . Ідея полягає в тому, що незалежно від того, як ви впорядковуєте свої дані, слід ідентифікувати окремі точки даних за допомогою системи "координат даних". Ось уривок з недавньої публікації блогу Джона Маунда:

Вся система базується на двох примітивах чи операторах cdata :: moveValuesToRowsD () та cdata :: moveValuesToColumnsD (). Ці оператори мають зведені, незворотні, однокольорові кодування, переміщення, переміщення декількох рядків і стовпців та багато інших перетворень як прості спеціальні випадки.

Неважко записати багато різних операцій з точки зору примітивних даних про дані. Ці оператори можуть працювати в пам'яті або у великих масштабах даних (з базами даних та Apache Spark; для великих даних використовувати варіанти cdata :: moveValuesToRowsN () та cdata :: moveValuesToColumnsN ()). Перетворення управляються таблицею управління, яка сама є діаграмою (або зображенням) перетворення.

Спочатку побудуємо контрольну таблицю (детальніше див. Допис у блозі ), а потім виконаємо переміщення даних із рядків у стовпці.

library(cdata)
# first build the control table
pivotControlTable <- buildPivotControlTableD(table = dat1, # reference to dataset
                        columnToTakeKeysFrom = 'numbers', # this will become column headers
                        columnToTakeValuesFrom = 'value', # this contains data
                        sep="_")                          # optional for making column names

# perform the move of data to columns
dat_wide <- moveValuesToColumnsD(tallTable =  dat1, # reference to dataset
                    keyColumns = c('name'),         # this(these) column(s) should stay untouched 
                    controlTable = pivotControlTable# control table above
                    ) 
dat_wide

#>         name  numbers_1  numbers_2  numbers_3  numbers_4
#> 1  firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
#> 2 secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

1

набагато простіший спосіб!

devtools::install_github("yikeshu0611/onetree") #install onetree package

library(onetree)
widedata=reshape_toWide(data = dat1,id = "name",j = "numbers",value.var.prefix = "value")
widedata

        name     value1     value2     value3     value4
   firstName  0.3407997 -0.7033403 -0.3795377 -0.7460474
  secondName -0.8981073 -0.3347941 -0.5013782 -0.1745357

якщо ви хочете повернутися від широкого до довгого, змініть лише "Широкий" на "Довгий" і ніяких змін об'єктів не буде.

reshape_toLong(data = widedata,id = "name",j = "numbers",value.var.prefix = "value")

        name numbers      value
   firstName       1  0.3407997
  secondName       1 -0.8981073
   firstName       2 -0.7033403
  secondName       2 -0.3347941
   firstName       3 -0.3795377
  secondName       3 -0.5013782
   firstName       4 -0.7460474
  secondName       4 -0.1745357
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.