【问题标题】:R: Combine duplicate rows and sequence date orderR:合并重复行和序列日期顺序
【发布时间】:2018-10-21 18:10:43
【问题描述】:

在四处寻找答案后第一次发帖。我的问题与展平重复项和按日期顺序保持数据有关。

在这个项目中,参与者来进行后续评估,并在 20 周内记录他们的体重。由于参与者在不同的日期到达进行评估,我们有 20 列考勤编号,其中包含该考勤日期,然后是 20 列他们在该特定考勤时的体重。不知何故,参与者被重复了,他们的措施脱节了。

编辑:我忘了说每个参与者都有一个唯一的 ID,所以在下面的例子中,p01 是一个参与者,但输入错误意味着他们出现了两次。

此外,我的意思是“扁平化”,有没有一种方法可以合并这些重复的条目,这样每个参与者只有一个观察/行。导致一些悲伤的是弄清楚如何排序每周数据,然后还要保留他们记录的体重值

一个更简单的虚构示例如下所示:

**What actually happened**
id  attendance_1  attendance_2   attendance_3  attendance 4  attendance 5    
p01  2018-05-01    2018-05-08     2018-05-15    NA            2018-05-28

     Weight_1   Weight_2  Weight_3  Weight_4  Weight_5
     179        176       178       NA        173

**What is recorded in dataset**
df
------
   id  attendance_1  attendance_2   attendance_3  attendance 4  attendance 5    
1 p01  2018-05-01    2018-05-08     2018-05-15    NA            NA
2 p01  2018-05-28    NA             NA            NA            NA

   Weight_1   Weight_2  Weight_3  Weight_4  Weight_5
1  179        176       178       NA        NA
2  173        NA        NA        NA        NA

因此参与者“p01”参加了 5 次评估中的 4 次,但他们的记录已重复,并且他们的“第五次”参加在他们的重复条目中显示为他们的“第一次”。有没有办法“压平”他们的记录以正确排列出勤日期和体重记录?

在此示例中,我正在考虑使用 lubridate 包根据从开始日期起的 5 周创建一个“结束日期”列,但我不知道如何在其“正确”空间中编写移动值,或者如果这是可能的。

我之前在其他分析中使用过此代码;

df_merged = aggregate(x = df, by = list(df$participants_ID), FUN = function(x) na.omit(x)[1])[,-1]

根据他们的 ID 展平重复的参与者,但在这种情况下这会覆盖数据并且不起作用。

提前谢谢你!

【问题讨论】:

  • 你怎么知道第二行的出勤_1实际上对应的是第一行的出勤_5而不是出勤_4?据我所知,您的数据中没有任何内容表明这一点。
  • 你能生成数据吗?也许像 20 行这样的代码将有助于创建一些代码而不是几个观察。
  • 欢迎来到stackoverflow。你很好地组织了你的问题。我认为到目前为止没有人回答你的原因有两个。首先,“扁平化”是什么意思?充其量给出一个你所拥有的虚构例子,并准确地展示你想要的结果(参见stackoverflow.com/questions/50291474/…)。其次,如果您向他们提供如何创建虚构数据的代码,人们往往会更频繁地回答您的问题。
  • 大家好,谢谢你们的cmets。自从发帖以来我一直不太好,所以今天才回到办公室,但感谢所有 cmets 的建议。 @SimonLarsen:因此,评估中的每个参与者都被赋予了一个唯一的标识符(它也只是在这个试点中发生,我们有少数参与者来跟踪 ID),并且在数据输入期间它们以某种方式出现在两行

标签: r sorting date duplicates


【解决方案1】:

要处理这种类型的数据清理问题,您可以查看tidyverse 包,特别是tidyrdplyr

我根据您的数据和描述重新创建了几个示例记录,并添加了第二个患者来帮助检查解决方案。使用此记录布局,appearance 数据以键值格式放入,然后是 weight 数据。使用为记录和患者 ID 生成的记录编号,将这两个部分连接在一起并省略 NA 行。

代码如下:

   library(tidyverse)
#
# assume that data for 2 patients would have following format
#

df <- read.table(header=TRUE, stringsAsFactors = FALSE, strip.white = TRUE,
             colClasses = c("character", rep("Date",5), rep("numeric",5)), 
text =  "id  attendance_1  attendance_2   attendance_3  attendance_4  attendance_5  Weight_1   Weight_2  Weight_3  Weight_4  Weight_5  
p01  2018-05-01    2018-05-08     2018-05-15    NA            NA           179        176       178       NA        NA
p02  2018-05-01    2018-05-08     2018-05-16  2018-05-22      NA           209        206       208      205        NA
p01  2018-05-28    NA             NA            NA            NA           173        NA        NA        NA        NA
p02  2018-05-28    NA             NA            NA            NA           203        NA        NA        NA        NA")

#
#  put attendance records in key-value format with record number and att_no
#
  df_att <- df %>%  select(id,attendance_1:attendance_5) %>% 
              gather(key = attendance, value = Date,  attendance_1:attendance_5) %>%
              mutate( rec_no = 1:n()) %>% 
              select(-attendance) %>% arrange(id, Date) %>% 
              group_by(id) %>% na.omit() %>% mutate(att_no = 1:n())
#
#  put weight records in key-value format with record number
#
  df_wt <- df %>%  select(id, Weight_1:Weight_5) %>% 
              gather(key = Weighing, value = weight, Weight_1:Weight_5) %>%
              mutate( rec_no = 1:n()) %>%
              select(-Weighing) %>% arrange(id, rec_no) %>% 
              group_by(id) %>% na.omit() 
#
#  join attendance and weight records by id and rec_no
#
   df_tot <- left_join(df_att, df_wt, by = c("id", "rec_no")) %>% 
      arrange( id, Date)
#
#  use spread to transform back to original format forming column names from att_no
#
  df_att_spd <- df_tot %>% mutate(attendance = paste0("attendance_",att_no)) %>%
            select(id, Date,attendance) %>% 
            spread(key = attendance, value = Date)
  df_wt_spd <- df_tot %>% mutate(weighing = paste0("Weight_",att_no)) %>%
           select(id, weight, weighing ) %>%
           spread(key = weighing, value = weight)
 df_tot_spd <- left_join(df_att_spd, df_wt_spd, by = "id")

这给出了结果:

   id    attendance_1 attendance_2 attendance_3 attendance_4 attendance_5 Weight_1 Weight_2 Weight_3 Weight_4 Weight_5
  <chr> <date>       <date>       <date>       <date>       <date>          <dbl>    <dbl>    <dbl>    <dbl>    <dbl>
  p01   2018-05-01   2018-05-08   2018-05-15   2018-05-28   NA                179      176      178      173       NA
  p02   2018-05-01   2018-05-08   2018-05-16   2018-05-22   2018-05-28        209      206      208      205      203

如果我没有正确理解您的数据,请澄清。

【讨论】:

    【解决方案2】:

    很遗憾,OP 没有显示出预期的结果。因此,我将展示三种不同的方法。

    第一个将按照 OP 的要求将记录“展平”以正确排列出勤日期和体重记录

    第二个按照每周的规律顺序将值移动到正确的空间中,出勤间隔为 7 天。

    第三个​​是一种变体,用于处理与常规计划存在偏差的情况,即,如果出勤之间存在 6 天或 8 天的差异。

    1。将记录展平,不留空隙

    library(data.table)
    # read data
    df1 <- fread(
    "id   attendance_1  attendance_2   attendance_3  attendance_4  attendance_5    Weight_1   Weight_2  Weight_3  Weight_4  Weight_5
    p01  2018-05-01    2018-05-08     2018-05-15    NA            NA              179        176       178       NA        NA
    p01  2018-05-28    NA             NA            NA            NA              173        NA        NA        NA        NA")
    
    cols <- c("attendance", "Weight")
    # reshape from wide to long format with 2 measure vars simultaneously
    long <- melt(setDT(df1), measure.vars = patterns(cols), value.name = cols, na.rm = TRUE)
    # reshape to long format in order of attendance witout gaps
    dcast(long[order(attendance)], id ~ rowid(id), value.var = cols)
    
        id attendance_1 attendance_2 attendance_3 attendance_4 Weight_1 Weight_2 Weight_3 Weight_4
    1: p01   2018-05-01   2018-05-08   2018-05-15   2018-05-28      179      176      178      173
    

    请注意,data.table 包中的 melt()dcast() 函数允许同时重塑多个度量/值列。

    这相当于WaltS' solution,但要短得多。

    2。根据每周模式展平记录

    library(data.table)
    # read data
    df1 <- fread(
      "id   attendance_1  attendance_2   attendance_3  attendance_4  attendance_5    Weight_1   Weight_2  Weight_3  Weight_4  Weight_5
    p01  2018-05-01    2018-05-08     2018-05-15    NA            NA              179        176       178       NA        NA
    p01  2018-05-29    NA             NA            NA            NA              173        NA        NA        NA        NA")
    

    请注意,2018-05-28 已替换为 2018-05-29 以适应每周模式。

    cols <- c("attendance", "Weight")
    # reshape from wide to long format with 2 measure vars simultaneously
    long <- melt(setDT(df1), measure.vars = patterns(cols), value.name = cols, na.rm = TRUE)
    # coerce attendance from class character to class Date
    long[, attendance := as.Date(attendance)]
    # create date sequences for each id
    date_seq <- long[, .(attendance = seq(min(attendance, na.rm = TRUE), 
                                          by = "week", length.out = 5L)), by = id]
    # right join with date_seq to fill in gaps
    long2 <- long[date_seq, on = .(id, attendance)]
    

    如果所有 id 共享相同的开始日期,则有一种简化的方法可以重塑为宽格式:

    dcast(long2[order(attendance)], id ~ attendance)
    
        id 2018-05-01 2018-05-08 2018-05-15 2018-05-22 2018-05-29
    1: p01        179        176        178         NA        173
    

    如果 id 有单独的开始日期,我们需要有“中性”的列标题:

    dcast(long2[order(attendance)], id ~ rowid(id), value.var = cols)
    
        id attendance_1 attendance_2 attendance_3 attendance_4 attendance_5 Weight_1 Weight_2 Weight_3 Weight_4 Weight_5
    1: p01   2018-05-01   2018-05-08   2018-05-15   2018-05-22   2018-05-29      179      176      178       NA      173
    

    3。用空白和大致日期展平

    library(data.table)
    # read data
    df1 <- fread(
      "id   attendance_1  attendance_2   attendance_3  attendance_4  attendance_5    Weight_1   Weight_2  Weight_3  Weight_4  Weight_5
    p01  2018-05-01    2018-05-08     2018-05-15    NA            NA              179        176       178       NA        NA
    p01  2018-05-28    NA             NA            NA            NA              173        NA        NA        NA        NA")
    

    注意,2018-05-28 再次使用,与上次出席时间相隔 13 天

    cols <- c("attendance", "Weight")
    # reshape from wide to long format with 2 measure vars simultaneously
    long <- melt(setDT(df1), measure.vars = patterns(cols), value.name = cols, na.rm = TRUE)
    # coerce attendance from class character to class Date
    long[, attendance := as.Date(attendance)]
    # create date sequences for each id
    date_seq <- long[, .(date = seq(min(attendance, na.rm = TRUE), 
                                          by = "week", length.out = 5L)), by = id]
    # creat helper column for rolling join
    long2 <- long[, date := attendance][
      # rolling join to nearest date
      date_seq, on = .(id, date), roll = "nearest"]
    # set data of dates which are more than 1 day off of the regular weekly pattern to NA
    long2[abs(attendance - date) > 1, (cols) := NA]
    long2
    
        id variable attendance Weight       date
    1: p01        1 2018-05-01    179 2018-05-01
    2: p01        2 2018-05-08    176 2018-05-08
    3: p01        3 2018-05-15    178 2018-05-15
    4: p01        1       <NA>     NA 2018-05-22
    5: p01        1 2018-05-28    173 2018-05-29
    
    # reshape to wide format
    dcast(long2[order(date)], id ~ rowid(id), value.var = cols)
    
        id attendance_1 attendance_2 attendance_3 attendance_4 attendance_5 Weight_1 Weight_2 Weight_3 Weight_4 Weight_5
    1: p01   2018-05-01   2018-05-08   2018-05-15         <NA>   2018-05-28      179      176      178       NA      173
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-04
      • 1970-01-01
      • 1970-01-01
      • 2022-07-22
      • 1970-01-01
      • 2019-06-08
      • 1970-01-01
      相关资源
      最近更新 更多