26

我有一个遵循以下长模式的数据框:

   Name          MedName
  Name1    atenolol 25mg
  Name1     aspirin 81mg
  Name1 sildenafil 100mg
  Name2    atenolol 50mg
  Name2   enalapril 20mg

并且想得到下面(我不在乎我是否可以这样命名列,只想要这种格式的数据):

   Name   medication1    medication2      medication3
  Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
  Name2 atenolol 50mg enalapril 20mg             NA

通过这个站点,我已经熟悉了 reshape/reshape2 包,并且已经尝试了几次试图让它工作,但到目前为止都失败了。

当我尝试时,dcast(dataframe, Name ~ MedName, value.var='MedName')我只得到一堆作为药物名称标志的列(转置的值是 1 或 0)例如:

 Name  atenolol 25mg  aspirin 81mg
Name1              1             1
Name2              0             0 

在我融化数据集后,我也尝试了一个dcast(dataset, Name ~ variable),但这只是吐出以下内容(只计算每个人有多少药物):

 Name  MedName
Name1        3
name2        2

最后,我尝试融合数据,然后使用idvar="Name" timevar="variable"(其中所有只是 Mednames)进行整形,但这似乎不是为我的问题构建的,因为如果 idvar 有多个匹配项,reshape 只会采用第一个 MedName 并忽略休息。

有谁知道如何使用 reshape 或其他 R 函数来做到这一点?我意识到可能有一种方法可以通过一些 for 循环和条件来以更混乱的方式执行此操作,以基本上拆分和重新粘贴数据,但我希望有一个更简单的解决方案。非常感谢!

4

8 回答 8

18

使用包,这可以通过新rowid功能轻松解决:

library(data.table)
dcast(setDT(d1), 
      Name ~ rowid(Name, prefix = "medication"), 
      value.var = "MedName")

这使:

   Name    medication1     medication2       medication3
1 Name1  atenolol 25mg    aspirin 81mg  sildenafil 100mg
2 Name2  atenolol 50mg  enalapril 20mg              <NA>

另一种方法(1.9.7版本之前常用):

dcast(setDT(d1)[, rn := 1:.N, by = Name], 
      Name ~ paste0("medication",rn), 
      value.var = "MedName")

给出相同的结果。


类似的方法,但现在使用包:

library(dplyr)
library(tidyr)
d1 %>%
  group_by(Name) %>%
  mutate(rn = paste0("medication",row_number())) %>%
  spread(rn, MedName)

这使:

Source: local data frame [2 x 4]
Groups: Name [2]

    Name   medication1    medication2      medication3
  (fctr)         (chr)          (chr)            (chr)
1  Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
2  Name2 atenolol 50mg enalapril 20mg               NA
于 2016-02-07T13:58:01.190 回答
16

假设您的数据在对象中dataset

library(plyr)
## Add a medication index
data_with_index <- ddply(dataset, .(Name), mutate, 
                         index = paste0('medication', 1:length(Name)))    
dcast(data_with_index, Name ~ index, value.var = 'MedName')

##    Name   medication1    medication2      medication3
## 1 Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
## 2 Name2 atenolol 50mg enalapril 20mg             <NA>
于 2012-07-04T05:36:12.273 回答
15

timevar使用reshape. 在这里,我使用“沿”每个“名称”ave应用函数。seq_along

test <- data.frame(
Name=c(rep("name1",3),rep("name2",2)),
MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg",
          "atenolol 50mg","enalapril 20mg")
)

# generate the 'timevar'
test$uniqid <- with(test, ave(as.character(Name), Name, FUN = seq_along))

# reshape!
reshape(test, idvar = "Name", timevar = "uniqid", direction = "wide")

结果:

   Name     MedName.1      MedName.2        MedName.3
1 name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
4 name2 atenolol 50mg enalapril 20mg             <NA>
于 2012-07-04T06:03:28.837 回答
11

这似乎实际上是一个相当普遍的问题,所以我getanID在我的“splitstackshape”包中包含了一个调用的函数。

这是它的作用:

library(splitstackshape)
getanID(test, "Name")
#     Name          MedName .id
# 1: name1    atenolol 25mg   1
# 2: name1     aspirin 81mg   2
# 3: name1 sildenafil 100mg   3
# 4: name2    atenolol 50mg   1
# 5: name2   enalapril 20mg   2

由于“data.table”与“splitstackshape”一起加载,您可以访问dcast.data.table,因此您可以像@mnel 的示例一样继续。

dcast.data.table(getanID(test, "Name"), Name ~ .id, value.var = "MedName")
#     Name             1              2                3
# 1: name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
# 2: name2 atenolol 50mg enalapril 20mg               NA

该函数本质上实现了sequence(.N)由标识的组创建“时间”列。

于 2014-12-15T09:58:52.307 回答
5

@thelatemail 的解决方案与此类似。当我生成时间变量时,我会使用rle以防我不是以交互方式工作并且Name变量需要是动态的。

# start with your example data
x <- 
    data.frame(
        Name=c(rep("name1",3),rep("name2",2)),
        MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg",
            "atenolol 50mg","enalapril 20mg")
    )

# pick the id variable
id <- 'Name'

# sort the data.frame by that variable
x <- x[ order( x[ , id ] ) , ]

# construct a `time` variable on the fly
x$time <- unlist( lapply( rle( as.character( x[ , id ] ) )$lengths , seq_len ) )

# `reshape` uses that new `time` column by default
y <- reshape( x , idvar = id , direction = 'wide' )

# done
y
于 2014-08-13T06:55:39.177 回答
4

一个干净的解决方案涉及包版本中非常有用pivot_wider的功能。有了这个,您还可以使用参数直接指定列名。tidyr1.1.0names_glue

library(tidyr)
library(dplyr)

dataframe %>% 
  group_by(Name) %>% 
  mutate(row_n = row_number()) %>% 
  pivot_wider(id_cols = Name, names_from = row_n, values_from = MedName, names_glue = "medication{row_n}")

输出

# A tibble: 2 x 4
# Groups:   Name [2]
#   Name  medication1   medication2    medication3     
#   <chr> <chr>         <chr>          <chr>           
# 1 Name1 atenolol 25mg aspirin 81mg   sildenafil 100mg
# 2 Name2 atenolol 50mg enalapril 20mg NA  
于 2020-07-14T11:16:10.693 回答
1

和的tidyr解决方案。chop()unnest_wider()

library(tidyr)

df %>%
  chop(-Name) %>%
  unnest_wider(MedName, names_sep = "")

# # A tibble: 2 x 4
#   Name  MedName1      MedName2       MedName3        
#   <chr> <chr>         <chr>          <chr>           
# 1 Name1 atenolol 25mg aspirin 81mg   sildenafil 100mg
# 2 Name2 atenolol 50mg enalapril 20mg NA 

论证names_sep = ""是必要的;否则,新列名将是..1..2..3


数据

df <- structure(list(Name = c("Name1", "Name1", "Name1", "Name2", "Name2"
), MedName = c("atenolol 25mg", "aspirin 81mg", "sildenafil 100mg", 
"atenolol 50mg", "enalapril 20mg")), class = "data.frame", row.names = c(NA, -5L))
于 2020-07-19T10:38:30.553 回答
0

这是一个更短的方法,利用unlist处理名称的方式:

library(dplyr)
df1 %>% group_by(Name) %>% do(as_tibble(t(unlist(.[2]))))
# # A tibble: 2 x 4
# # Groups:   Name [2]
#      Name      MedName1       MedName2         MedName3
#     <chr>         <chr>          <chr>            <chr>
#   1 name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
#   2 name2 atenolol 50mg enalapril 20mg             <NA>
于 2017-10-06T22:06:33.897 回答