вставлять значения в категории, определенные несколькими столбцами

Я хочу повернуть столбец result в df по горизонтали, создав набор данных с отдельной строкой для каждой комбинации region, state, county, где столбцы упорядочены по year, а затем по city.

Я также хочу идентифицировать каждую строку в новом наборе данных с помощью region, state и county и удалить пробел между четырьмя столбцами results. Приведенный ниже код делает все это, но я подозреваю, что он не очень эффективен.

Есть ли способ сделать это с помощью reshape2 без создания уникального идентификатора для каждой группы и нумерации наблюдений внутри каждой группы? Есть ли способ использовать apply вместо цикла for для удаления пробелов из матрицы? (Здесь матрица используется иначе, чем математическая или программная конструкция.) Я понимаю, что это два отдельных вопроса, и, возможно, мне следует публиковать каждый вопрос отдельно.

Учитывая, что я могу добиться желаемого результата и хочу только улучшить код, я не знаю, стоит ли мне вообще публиковать это, но я надеюсь научиться. Спасибо за любой совет.

df <- read.table(text= "
region   state    county city  year result
1          1        1      1     1     1
1          1        1      2     1     2
1          1        1      1     2     3
1          1        1      2     2     4
1          1        2      3     1     4
1          1        2      4     1     3
1          1        2      3     2     2
1          1        2      4     2     1
1          2        1      1     1     0
1          2        1      2     1    NA
1          2        1      1     2     0
1          2        1      2     2     0
1          2        2      3     1     2
1          2        2      4     1     2
1          2        2      3     2     2
1          2        2      4     2     2
2          1        1      1     1     9
2          1        1      2     1     9
2          1        1      1     2     8
2          1        1      2     2     8
2          1        2      3     1     1
2          1        2      4     1     0
2          1        2      3     2     1
2          1        2      4     2     0
2          2        1      1     1     2
2          2        1      2     1     4
2          2        1      1     2     6
2          2        1      2     2     8
2          2        2      3     1     3
2          2        2      4     1     3
2          2        2      3     2     2
2          2        2      4     2     2
", header=TRUE, na.strings=NA)

desired.result <- read.table(text= "
region   state    county results
1          1        1     1234
1          1        2     4321
1          2        1     0.00
1          2        2     2222
2          1        1     9988
2          1        2     1010
2          2        1     2468
2          2        2     3322
", header=TRUE, colClasses=c('numeric','numeric','numeric','character'))

# redefine variables for package reshape2 creating a unique id for each
# region, state, county combination and then number observations in
# each of those combinations

library(reshape2)

id.var <- df$region*100000 + df$state*1000 + df$county
obsnum <- sequence(rle(id.var)$lengths)

df2 <- dcast(df, region + state + county ~ obsnum, value.var = "result")

# remove spaces between columns of results matrix
# with a for-loop.  How can I use apply to do this?

x <- df2[,4:(4+max(obsnum)-1)]

# use a dot to represent a missing observation

x[is.na(x)] = '.'

x.cat = numeric(nrow(x))

for(i in 1:nrow(x)) {
  x.cat[i] = paste(x[i,], collapse="")
}

df3 <- cbind(df2[,1:3],x.cat)
colnames(df3) <- c("region", "state", "county", "results")
df3

df3 == desired.result

РЕДАКТИРОВАТЬ:

Ответ Мэтью Лундберга ниже превосходен. Впоследствии я понял, что мне также необходимо создать набор выходных данных, в котором четыре столбца результатов выше содержат числовые, рациональные числа и разделены пробелом. Итак, я опубликовал очевидный способ сделать это ниже, который изменяет ответ Мэтью. Я не знаю, является ли это принятым протоколом, но новый сценарий кажется настолько непосредственно связанным с исходным сообщением, что я не подумал, что должен публиковать новый вопрос.


person Mark Miller    schedule 31.12.2012    source источник


Ответы (3)


Я думаю, что это делает то, что вы хотите:

df$result <- as.character(df$result)
df$result[is.na(df$result)] <- '.'


aggregate(result ~ county+state+region, data=df, paste0, collapse='')

  county state region result
1      1     1      1   1234
2      2     1      1   4321
3      1     2      1   0.00
4      2     2      1   2222
5      1     1      2   9988
6      2     1      2   1010
7      1     2      2   2468
8      2     2      2   3322

Это зависит от того, сортируется ли ваш фрейм данных в правильном порядке (как у вас).

person Matthew Lundberg    schedule 31.12.2012
comment
Спасибо за выдающийся ответ. Позже я понял, что мне также нужен набор выходных данных, в котором четыре столбца результатов являются числовыми и разделены пробелом. Я не мог изменить ваш ответ, чтобы сделать это, но я подошел близко и разместил код здесь. - person Mark Miller; 01.01.2013

Ответ Мэтью Лундберга превосходен. Впоследствии я понял, что мне также необходимо создать набор выходных данных, в котором четыре столбца результатов выше содержат числовые, рациональные числа и разделены пробелом. Итак, здесь я предлагаю очевидный способ сделать это, используя модификацию ответа Мэтью. Я не знаю, является ли это принятым протоколом, но новый сценарий кажется настолько непосредственно связанным с исходным сообщением, что я не подумал, что должен публиковать новый вопрос.

Первые две строки являются модификациями ответа Мэтью.

df$result[is.na(df$result)] <- 'NA'
df2 <- aggregate(result ~ county+state+region, data=df, paste)

Затем я указываю, что NA представляет отсутствующие наблюдения, и использую apply для получения числового вывода.

df2$result[df2$result=='NA'] = NA
new.df <- data.frame(df2[,1:3], apply(df2$result,2,as.numeric))

Вывод ниже, за исключением того, что я добавил 0,5 к каждому значению в df, показанному в исходном сообщении.

  county state region  X1  X2  X3  X4
     1     1      1   1.5 2.5 3.5 4.5
     2     1      1   4.5 3.5 2.5 1.5
     1     2      1   0.5  NA 0.5 0.5
     2     2      1   2.5 2.5 2.5 2.5
     1     1      2   9.5 9.5 8.5 8.5
     2     1      2   1.5 0.5 1.5 0.5
     1     2      2   2.5 4.5 6.5 8.5
     2     2      2   3.5 3.5 2.5 2.5
person Mark Miller    schedule 01.01.2013

В своем исходном посте я спрашивал, как удалить пробелы между столбцами в наборе данных, используя apply. В этом не оказалось необходимости благодаря ответу Мэтью Лундберга на мой более крупный вопрос. Тем не менее удаление пробелов между столбцами набора данных — это то, что мне часто приходится делать. Для полноты здесь я публикую способ сделать это, используя paste0 и apply, который частично возник из ответа Мэтью.

Чтобы удалить все пробелы из набора данных x:

x <- read.table(text= "
A    B    C    D
1    1    1    1
1    1    2    2
1   NA    1    3
1    1    2    4
1    2    1    5
1    2   NA    6
1    2    1    7
1    2    2    8
", header=TRUE, na.strings=NA)

# use a dot to represent a missing observation

x[is.na(x)] = '.'

y <- as.data.frame(apply(x, 1, function(i) paste0(i, collapse='')))
colnames(y) <- 'result'
y

Дает:

  result
1   1111
2   1122
3   1.13
4   1124
5   1215
6   12.6
7   1217
8   1228

Следующий код удаляет пробелы только между вторым и третьим столбцами:

z <- as.data.frame(apply(x[,2:3], 1, function(i) paste0(i, collapse='')))

y <- data.frame(x[,1], z, x[,4])
colnames(y) <- c('A','BC','D')
y

Предоставление:

  A BC D
1 1 11 1
2 1 12 2
3 1 .1 3
4 1 12 4
5 1 21 5
6 1 2. 6
7 1 21 7
8 1 22 8
person Mark Miller    schedule 01.01.2013
comment
Нет необходимости создавать анонимные функции для apply. Вместо этого используйте аргумент ... для перехода к paste0. apply(x, 1, paste0, collapse=''), - person Matthew Lundberg; 02.01.2013