在 R 中推断顺序数据中的选择顺序

Inferring choice order in sequential data, in R

我有一个数据框,其中的选择是在会话中按顺序进行的。我想创建一个变量来指示每个选择的订单号。问题是每次session我只知道第一个选择是什么,我想知道每个选择的顺序

所以假设我们有一个选择和一个信号告诉我们这是否是会话中的第一选择。还假设数据是有序的。我想要的是获得表示选择顺序的第三列(顺序),这样每次我们有一个 1,顺序就是 1,并且它会上升(2、3,...)直到下一个 1 .

df = data.frame(
  choice = c('a','a','b','e','a','l','d','a'),
  signal = c(1,0,0,1,0,0,0,0),
  order = c(1,2,3,1,2,3,4,5))

  choice signal order
1      a      1     1
2      a      0     2
3      b      0     3
4      e      1     1
5      a      0     2
6      l      0     3
7      d      0     4
8      a      0     5

所以我尝试用地图解决这个问题,但它没有奏效,原因很明显:我不知道如何更新地图外的矢量。

my_order = df$signal
map(
  .x = seq(1,(df$signal %>% length())),
  .f = function(x) {
    my_order[x] = ifelse(my_order[x]==1, my_order[x], my_order[x-1]+1)
    my_order})

知道如何使用地图执行此操作吗?用别的东西?我试图避免 for i.

您可以使用 ave 并从 cumsum(signal == 1) 定义的组中创建一个序列 seq_along (或者只是 cumsum(signal) 因为它只有 0-1 个值;正如所指出的出自@philliptomk)。

df$order <- with(df, ave(signal, cumsum(signal == 1), FUN = seq_along))

df
#   choice signal order
# 1      a      1     1
# 2      a      0     2
# 3      b      0     3
# 4      e      1     1
# 5      a      0     2
# 6      l      0     3
# 7      d      0     4
# 8      a      0     5

或使用 group_byrow_number 来自 dplyr:

library(dplyr)
df %>% 
  group_by(gp = cumsum(signal == 1)) %>% 
  mutate(order = row_number())

或使用data.table::rowid:

data.table::rowid(cumsum(df$signal == 1))

您可以使用split-apply-combine策略:


df <- unsplit(lapply(split(df,
                     cumsum(df$signal) # split according to cumulativesum
                     ),function(x) {
                        x$order = c(1:nrow(x))
                        return(x)}
               ),
        cumsum(df$signal) # reattach the splits to single dataframe
      )
# choice signal order
# 1      a      1     1
# 2      a      0     2
# 3      b      0     3
# 4      e      1     1
# 5      a      0     2
# 6      l      0     3
# 7      d      0     4
# 8      a      0     5

另一种可能的解决方案,基于purrr::reduce

library(tidyverse)

df$order2 <- reduce(df$signal, ~ if (.y == 0) {c(.x, .x[length(.x)]+1)} 
                    else {c(.x, 1)})

df

#>   choice signal order order2
#> 1      a      1     1      1
#> 2      a      0     2      2
#> 3      b      0     3      3
#> 4      e      1     1      1
#> 5      a      0     2      2
#> 6      l      0     3      3
#> 7      d      0     4      4
#> 8      a      0     5      5

另一种可能的解决方案,基于dplyr:

library(dplyr)

df %>% 
  group_by(aux = data.table::rleid(signal)) %>% 
  mutate(order2 = ifelse(signal == 0, 1 + row_number(), signal)) %>% 
  ungroup %>% 
  select(-aux)

#> # A tibble: 8 × 4
#>   choice signal order order2
#>   <chr>   <dbl> <dbl>  <dbl>
#> 1 a           1     1      1
#> 2 a           0     2      2
#> 3 b           0     3      3
#> 4 e           1     1      1
#> 5 a           0     2      2
#> 6 l           0     3      3
#> 7 d           0     4      4
#> 8 a           0     5      5

假设信号在第一行每次都是 1:使用 rle.

df$order <- sequence(rle(cumsum(df$signal))$length)
df
#  choice signal order
#1      a      1     1
#2      a      0     2
#3      b      0     3
#4      e      1     1
#5      a      0     2
#6      l      0     3
#7      d      0     4
#8      a      0     5

whichdiff

sequence(diff(c(which(df$signal==1)-1, nrow(df))))
#[1] 1 2 3 1 2 3 4 5