Ответ 1
Использование функции reshape
:
reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")
У меня возникли проблемы с перестановкой следующего фрейма данных:
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 называлась rowname, а "значения" - как наблюдения вдоль этой строки, а "числа" - как colnames. Пример:
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
и несколько других вещей, но никто, похоже, не выполняет эту работу.
Использование функции reshape
:
reshape(dat1, idvar = "name", timevar = "numbers", direction = "wide")
Новый (в 2014 году) пакет tidyr
также делает это просто, при этом gather()
/spread()
являются терминами для melt
/cast
.
Изменить: Теперь, в 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
не предоставляет полей или агрегирования.
Вы можете сделать это с помощью функции reshape()
или с помощью функций melt()
/cast()
в пакете reshape. Для второго варианта код примера
library(reshape)
cast(dat1, name ~ numbers)
Или используя reshape2
library(reshape2)
dcast(dat1, name ~ numbers)
Другим вариантом, если производительность является проблемой, является использование расширения data.table
reshape2
функций таяния и 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
Используя ваш пример dataframe, мы могли бы:
xtabs(value ~ name + numbers, data = dat1)
Другие два варианта:
Базовый пакет:
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')
Используя базовую 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
В Win-Vector vtreat
очень мощный новый пакет от ученых-гениальных данных (vtreat
, seplyr
и replyr
) под названием cdata
. Он реализует принципы "скоординированных данных", описанные в этом документе, а также в этом блоге. Идея состоит в том, что независимо от того, как вы организуете свои данные, должна быть возможность идентифицировать отдельные точки данных, используя систему "координат данных". Вот выдержка из недавнего сообщения в блоге Джона Маунта:
Вся система основана на двух примитивах или операторах cdata :: moveValuesToRowsD() и cdata :: moveValuesToColumnsD(). Эти операторы имеют возможность вращения, разворота, горячего кодирования, транспонирования, перемещения нескольких строк и столбцов и многих других преобразований в виде простых особых случаев.
Легко написать много разных операций в терминах примитивов cdata. Эти операторы могут работать в оперативной памяти или в большом масштабе данных (с базами данных и 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
Функция базового 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
и 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
проста. Вывод этой функции более предсказуем, чем некоторые другие, так как все явно определено.
В версии tidyr
'0.8.3.9000
есть pivot_wider
и pivot_longer
которые обобщенно выполняют преобразование (long → wide, wide → long соответственно) из 1 в несколько столбцов. Использование данных OP
-single длинный столбец → широкий
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
гораздо проще!
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