R: Добавление NA во фрейм данных

У меня есть кадр данных, например:

Name   Position   Value
a         1        0.2
a         3        0.4
a         4        0.3
b         1        0.5
b         2        0.4
b         5        0.3
c         2        0.3
c         3        0.4
c         5        0.1
d         1        0.2
d         2        0.4
d         3        0.5

Я хочу сделать так, чтобы позиция всегда менялась от 1 до 5 для каждого имени и заполнила NA в значение следующим образом:

Name   Position   Value
a         1        0.2
a         2        NA
a         3        0.4
a         4        0.3
a         5        NA
b         1        0.5
b         2        0.4
b         3        NA
b         4        NA
b         5        0.3
c         1        NA
c         2        0.3
c         3        0.4
c         4        NA
c         5        0.1
d         1        0.2
d         2        0.4
d         3        0.5
d         4        NA
d         5        NA

Есть ли способ сделать это, не создавая фиктивный фрейм данных с первыми двумя столбцами, а затем выполняя какое-то внешнее соединение со слиянием?

Спасибо.


person Cinji18    schedule 27.10.2014    source источник
comment
@ Cinji18 Использовать merge очень просто df1 <- expand.grid(Name=unique(df$Name), Position=1:5); merge(df1, df,all=TRUE)   -  person akrun    schedule 27.10.2014
comment
@akrun Опс ... не увидел ваш комментарий и отправил тот же ответ ... Я удалил его.   -  person nicola    schedule 27.10.2014
comment
@никола Все в порядке. Вы можете опубликовать это. Он более компактный, чем мой код, и немного общий. Я не публиковал решение, потому что ОП упомянул о том, что не использует merge   -  person akrun    schedule 27.10.2014


Ответы (5)


Вы можете использовать пакет reshape2:

# make sample data frame
df <- read.table(text = "Name   Position   Value
a         1        0.2
a         3        0.4
a         4        0.3
b         1        0.5
b         2        0.4
b         5        0.3
c         2        0.3
c         3        0.4
c         5        0.1
d         1        0.2
d         2        0.4
d         3        0.5", header = TRUE, stringsAsFactors = FALSE)

library('reshape2')
df2 <- dcast(df, Name ~ Position)
df3 <- melt(df2, value.name = "Value", variable.name = "Position")
df3[order(df3$Name), ]
#    Name Position Value
# 1     a        1   0.2
# 5     a        2    NA
# 9     a        3   0.4
# 13    a        4   0.3
# 17    a        5    NA
# 2     b        1   0.5
# 6     b        2   0.4
# 10    b        3    NA
# 14    b        4    NA
# 18    b        5   0.3
# 3     c        1    NA
# 7     c        2   0.3
# 11    c        3   0.4
# 15    c        4    NA
# 19    c        5   0.1
# 4     d        1   0.2
# 8     d        2   0.4
# 12    d        3   0.5
# 16    d        4    NA
# 20    d        5    NA
person Kara Woo    schedule 27.10.2014

Я бы использовал data.table, но по-другому, как подчеркнул @akrun:

library(data.table)
dt = as.data.table(df)
setkey(dt, Name, Position)
dt[CJ(unique(Name),unique(Position))]
person Colonel Beauvel    schedule 27.10.2014

Возможно, это излишне, но я думаю, что вы можете использовать sqldf для сделай это:

library(sqldf)
# Your data frame:
df <- data.frame(
  name = c('a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd'),
  position = c(1, 3, 4, 1, 2, 5, 2, 3, 5, 1, 2, 3),
  value = c(0.2, 0.4, 0.3, 0.5, 0.4, 0.3, 0.3, 0.4, 0.1, 0.2, 0.4, 0.5)
)
# A data frame to hold the positions you want to fill:
pos = data.frame(pos = 1:5)
# SQLdf let's you write SQL sentences that use data frames like SQL tables:
df2 <- sqldf(
  "select a.*, b.value as value
  from (
    select a.name, p.pos as position 
    from (select distinct name from df) as a, pos as p
  ) as a
  left join df as b on a.name = b.name and a.position = b.position"
)
df2
## Result:
##   name position value
##1     a        1   0.2
##2     a        2    NA
##3     a        3   0.4
##4     a        4   0.3
##5     a        5    NA
##6     b        1   0.5
##7     b        2   0.4
##8     b        3    NA
##9     b        4    NA
##10    b        5   0.3
##11    c        1    NA
##12    c        2   0.3
##13    c        3   0.4
##14    c        4    NA
##15    c        5   0.1
##16    d        1   0.2
##17    d        2   0.4
##18    d        3   0.5
##19    d        4    NA
##20    d        5    NA

Конечно, вы можете назначить результат sqldf() непосредственно df, чтобы перезаписать исходный фрейм данных.

person Barranka    schedule 27.10.2014

Вы можете использовать data.table

 library(data.table)
 DT <- data.table(df)
 setkey(DT, Position)
 DT[, .SD[J(1:5), roll=FALSE], by=Name][order(Name, Position),]
 #   Name Position Value
 #1:    a        1   0.2
 #2:    a        2    NA
 #3:    a        3   0.4
 #4:    a        4   0.3
 #5:    a        5    NA
 #6:    b        1   0.5
 #7:    b        2   0.4
 #8:    b        3    NA
 #9:    b        4    NA
#10:    b        5   0.3
#11:    c        1    NA
#12:    c        2   0.3
#13:    c        3   0.4
#14:    c        4    NA
#15:    c        5   0.1
#16:    d        1   0.2
#17:    d        2   0.4
#18:    d        3   0.5
#19:    d        4    NA
#20:    d        5    NA

Или вы можете использовать tidyr/dplyr

 library(dplyr)
 library(tidyr)

  df %>% 
      spread(Position, Value) %>%
      gather(Position, Value, `1`:`5`) %>%
      arrange(Name, Position)    

данные

 df <- structure(list(Name = c("a", "a", "a", "b", "b", "b", "c", "c", 
 "c", "d", "d", "d"), Position = c(1L, 3L, 4L, 1L, 2L, 5L, 2L, 
 3L, 5L, 1L, 2L, 3L), Value = c(0.2, 0.4, 0.3, 0.5, 0.4, 0.3, 
 0.3, 0.4, 0.1, 0.2, 0.4, 0.5)), .Names = c("Name", "Position", 
 "Value"), class = "data.frame", row.names = c(NA, -12L))
person akrun    schedule 27.10.2014
comment
Это не эффективный способ сделать это. - person Arun; 27.10.2014
comment
@akrun Могу ли я задать вопрос, связанный с CJ и SJ? Сегодня я рассматривал пример CJ. Теперь я ищу сообщения SO с SJ. Но я действительно не вижу ни одного. Вы использовали SJ раньше? Если да, то как вы его использовали? - person jazzurro; 28.10.2014
comment
@jazzurro Нет, я раньше не использовал SJ. Я не знаю, видели ли вы эту ссылку раньше r.789695.n4.nabble.com/What-is-the-point-of-SJ-td4670616.html - person akrun; 28.10.2014
comment
@akrun Большое спасибо. Кажется, что SJ действительно для сортировки. Вероятно, это как arrange. Видя не так много постов, SJ может быть не таким уж полезным? - person jazzurro; 28.10.2014
comment
@akrun Да, я только что посмотрел на это перед тем, как пришло ваше сообщение. Это единственное, что я пока могу найти. - person jazzurro; 28.10.2014
comment
@jazzurro В некоторых случаях это может быть быстрее, как описано в посте. - person akrun; 28.10.2014
comment
@akrun Думаю, это преимущество. Сейчас я сосредоточусь на изучении CJ. Большое спасибо, что нашли время. - person jazzurro; 28.10.2014

Вот несколько базовых решений:

as.data.frame.table(tapply(df[[3]], df[2:1], c))

и

merge(df, 
      expand.grid(Position = unique(df$Position), Name = unique(df$Name)), 
      all = TRUE)
person G. Grothendieck    schedule 27.10.2014