【问题标题】:Selecting unique values without repeating colums选择唯一值而不重复列
【发布时间】:2018-10-31 15:41:22
【问题描述】:

这是一个非常具体的问题:我有一组观察结果,来自多个科目,历时数年(每年只有一次观察)。我只想为每个人选择一个观察值(我不在乎哪一年),这样我每年的观察结果数量相似,并且尽可能随机。

因此,从df 开始,其中 1 是对该个人进行观察的年份,而 0 是对该个人没有观察的年份:

df <- data.frame(Ind   = c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"),
             Year1 = c(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0), 
             Year2 = c(0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0), 
             Year3 = c(1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1), 
             Year4 = c(0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1))

看起来像

我想以这样的方式结束

编辑:尝试应用提出的解决方案(但失败)

(1) 搜索者的回答:

df <- as_tibble(df)

year.weights <- df %>% 
  gather(Year, Obs, -Ind) %>% 
  group_by(Year) %>% 
  summarize(wt = sum(Obs)) %>% 
  ungroup


df %>% 
      gather(Year, Obs, -Ind) %>%
      filter(Obs == 1) %>% 
      left_join(year.weights, by = "Year") %>% 
      group_by(Ind) %>% 
      sample_n(1, weight = 1 / wt) %>% 
      select(-wt) %>% 
      spread(Year, Obs) %>% 
      ungroup

这会产生错误Error: 'by' can't contain join column 'Year' which is missing from RHS,出现在left_join 步骤中。我尝试通过将名称“Year”赋予 RHS 中唯一的变量来解决这个问题

names(year.weights) &lt;- "Year"

但是现在这给出了一个新错误:Error in left_join_impl(x, y, by_x, by_y, aux_x, aux_y, na_matches) : Can't join on 'Year' x 'Year' because of incompatible types (numeric / character) 这实际上很有意义,因为 LHS 中的 Year 列包含 Year1、Year2、Year3 等,而 RHS 中的 Year 列包含数字 27。

这是我所得到的,因为我看不到 earch 试图完成什么,但我相信通过这个 n_sample 和 weight 参数可以实现一个可行的解决方案,但我还不太清楚.

(2) 米奇的回答:

这很好用(我没有得到我之前遇到的错误),但它不能保证我会为每个“年份”列得到相等(或相似)数量的 1。

所以,如果我运行代码几次进行测试,我会得到:

# first time
      [,1] [,2] [,3] [,4]
 [1,]    0    0    0    1
 [2,]    1    0    0    0
 [3,]    0    0    1    0
 [4,]    0    1    0    0
 [5,]    1    0    0    0
 [6,]    0    0    1    0
 [7,]    0    0    0    1
 [8,]    0    1    0    0
 [9,]    0    0    0    1
[10,]    0    0    0    1
[11,]    0    0    0    1

# second time
      [,1] [,2] [,3] [,4]
 [1,]    1    0    0    0
 [2,]    1    0    0    0
 [3,]    0    0    1    0
 [4,]    0    1    0    0
 [5,]    0    0    0    1
 [6,]    1    0    0    0
 [7,]    1    0    0    0
 [8,]    0    0    0    1
 [9,]    0    0    0    1
[10,]    0    0    0    1
[11,]    0    0    1    0

(3) Andre Elrico 的回答:

它与答案(2)有相同的问题,它不保证每年有相同数量的 1:查看两个随机输出:

# fist try
   Ind Year1 Year2 Year3 Year4
1    a    NA    NA    NA     1
2    b    NA    NA     1    NA
3    c    NA    NA     1    NA
4    d    NA     1    NA    NA
5    e     1    NA    NA    NA
6    f    NA    NA     1    NA
7    g     1    NA    NA    NA
8    h    NA    NA    NA     1
9    i    NA    NA    NA     1
10   j    NA    NA    NA     1
11   k    NA    NA     1    NA

# second try
   Ind Year1 Year2 Year3 Year4
1    a     1    NA    NA    NA
2    b     1    NA    NA    NA
3    c    NA    NA     1    NA
4    d    NA    NA     1    NA
5    e    NA     1    NA    NA
6    f    NA    NA    NA     1
7    g    NA    NA    NA     1
8    h    NA    NA    NA     1
9    i    NA    NA    NA     1
10   j    NA     1    NA    NA
11   k    NA    NA     1    NA

(4) paoloeusebi 的回答 和前面的问题一样。不保证每行选择的 1 数量相等:

# first try
   Ind Year1 Year2 Year3 Year4
1    a     1    NA    NA    NA
2    b    NA    NA    NA     0
3    c    NA    NA     1    NA
4    d    NA    NA    NA     0
5    e    NA    NA     1    NA
6    f    NA    NA    NA     1
7    g     1    NA    NA    NA
8    h    NA    NA     0    NA
9    i    NA    NA    NA     1
10   j    NA    NA    NA     1
11   k    NA    NA     1    NA

# second try
   Ind Year1 Year2 Year3 Year4
1    a    NA    NA    NA     1
2    b    NA     0    NA    NA
3    c    NA     1    NA    NA
4    d    NA    NA    NA     0
5    e    NA    NA    NA     1
6    f    NA     0    NA    NA
7    g    NA     0    NA    NA
8    h    NA    NA     0    NA
9    i    NA    NA     0    NA
10   j    NA    NA     0    NA
11   k    NA     0    NA    NA

【问题讨论】:

  • 您是否只想随机选择值为1的年份?
  • 在您的示例中,个人 e 最终有两个观察结果。我认为这是一个错误?
  • Mike 值将始终为 1,因为每年总会有一个 obs。 iod,不,如果有 4 年,但观察次数不是 4 的倍数,这可能会发生。这只会增加问题的复杂性。
  • 什么?你说你希望每个人一个观察。观察次数是四的倍数还是不是?我不明白这个问题。并且只有“1”被认为是观察?
  • 是的,我可能应该解释得更好,我会编辑它。 “输出”图像中也存在错误。只有 1 是观察值,如果是 0,那一年该个体没有观察到。是的,我想要每个人的 1 个观察值(一个 1),但是以某种方式,我最终每年都会得到相同(或相似)数量的观察值(1 秒)(即结果中的 rowSums矩阵是 c(3, 2, 3, 3) 但 colSums 是 c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1)

标签: r dataframe


【解决方案1】:

如果您想要每个人的随机年份为 1,那么这里有一个 dplyr/tidyr 方法:

> df <- data.frame(Ind   = c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"),
+                  Year1 = c(1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0), 
+                  Year2 = c(0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0), 
+                  Year3 = c(1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1), 
+                  Year4 = c(0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1))
> 
> year.weights <- df %>% 
+   gather(Year, Obs, -Ind) %>% 
+   group_by(Year) %>% 
+   summarize(wt = sum(Obs)) %>% 
+   ungroup
> 
> year.weights
# A tibble: 4 x 2
  Year     wt
  <chr> <dbl>
1 Year1     7
2 Year2     5
3 Year3     7
4 Year4     7
> 
> 
> df %>% 
+   gather(Year, Obs, -Ind) %>%
+   filter(Obs == 1) %>% 
+   left_join(year.weights, by = "Year") %>% 
+   group_by(Ind) %>% 
+   sample_n(1, weight = 1 / wt) %>% 
+   select(-wt) %>% 
+   spread(Year, Obs) %>% 
+   ungroup
# A tibble: 11 x 5
   Ind   Year1 Year2 Year3 Year4
   <fct> <dbl> <dbl> <dbl> <dbl>
 1 a         1    NA    NA    NA
 2 b        NA    NA     1    NA
 3 c        NA     1    NA    NA
 4 d         1    NA    NA    NA
 5 e        NA    NA     1    NA
 6 f         1    NA    NA    NA
 7 g        NA    NA    NA     1
 8 h        NA    NA    NA     1
 9 i        NA    NA    NA     1
10 j        NA     1    NA    NA
11 k        NA    NA    NA     1

【讨论】:

  • 我不知道这种 tidyr 随机抽样的方法,谢谢!但是,这样做的关键在于以所有列(年份)都具有相同(或最接近可能)数量的选定观察值的方式进行采样。
  • 那么你要对每一列进行加权。答案已被编辑为示例
【解决方案2】:

这里有一些代码。也许不是那么优雅,但这是一个开始:

new_mat = function(df, max_iter = 100){
    ind_names <- df[,1]
    df <- df[,-1]
    n = NROW(df)
    k = NCOL(df)
    max_col = ceiling(n / k)
    resample = function(x, ...) x[sample.int(length(x), ...)]
    one_hot = function(i, n){
        x = double(n)
        x[i] = 1
        return (x)
        }
    counter = 0
    flag = TRUE
    while (flag && counter <= max_iter){
        counter = counter + 1
        out = matrix(0, n, k)
        weights = rep(max_col, k)
        index = sample(1:n)
        c2 = 0
        for (i in index){
            ind = which(df[i,] == 1)
            probs = weights[ind]
            if (max(probs) == 0)
                break
            out[i,] = one_hot(resample(ind, size = 1, prob = probs), k)
            weights = weights - out[i,]
            c2 = c2 + 1
            }
        if (c2 == length(index))
            flag = FALSE
        }
    if (flag)
        stop('No matrix found. Try again.')
    final <- cbind(ind_names, as.data.frame(out))
    names(final) <- c("ind", names(df))
    return (final)
    }

如果您还想随机选择哪一列,则要求每列具有(大致)相同的出现次数会带来相当大的问题。当并非所有行在每列中都有观察值时,这尤其是一个问题。行与行之间存在隐含的依赖关系,这可能是不可取的。

基本上,一旦该列达到max_col,或者在此之后列不能具有大致相同数量的最大出现次数,这最终会将被选中的列的权重设置为零。 (我借用了 earch 对列加权的想法。)

如果出现问题(例如,无法为具有 weight&gt;0 的下一行选择任何列),则重新运行该过程,最多 max_iter,但会经历不同的顺序行。

这样做的一个主要缺点是必须重复循环遍历所有行。鉴于您的限制,我不确定解决此问题的方法。因此,如果您有一个非常大的数据框,您可能会看到一些较长的计算时间。但是在您提供的数据集上,该函数通常只经过一次就返回一个矩阵,不超过几次。

【讨论】:

  • 看起来很有希望!但它会抛出一个错误:运行最后一行时,sample.int(length(x), ...) 中的错误:第一个参数无效调用自:sample.int(length(x), ...)
  • 当我完全按照上述方式运行代码时(并使用您的df 分配),我没有收到任何错误。你怎么称呼最后一行?
  • 效果很好!如果每列中 1 的数量变化太大,则找不到矩阵,因此没有可行的解决方案可以提供每列相似数量的观察值,但除此之外它很好。我对您的代码进行了一些小的编辑,以避免 one_hot 函数将“ind”列作为包含数据的列,并接受了您的回答。非常感谢!
  • 如果您的问题得到解决,请choose an answer
【解决方案3】:

这里有一个解决方案,每个主题 3 年随机替换 4 年 NA

for (i in 1:dim(df)[1]){
    df[i,c(sample(2:5,3))]<-NA
    }

【讨论】:

    【解决方案4】:
    m   <- df[-1]
    IND <- rowSums(m) > 0
    m[] <- NA
    m[cbind(which(IND),max.col(df[-1])[IND])] <- 1
    cbind(df[1],m)
    

    结果:

    #   Ind Year1 Year2 Year3 Year4
    #1    a     1    NA    NA    NA
    #2    b    NA    NA     1    NA
    #3    c    NA    NA     1    NA
    #4    d    NA    NA     1    NA
    #5    e    NA    NA     1    NA
    #6    f     1    NA    NA    NA
    #7    g    NA    NA    NA     1
    #8    h    NA    NA    NA     1
    #9    i    NA    NA    NA     1
    #10   j    NA     1    NA    NA
    #11   k    NA    NA     1    NA
    

    如果您不喜欢简单地将变量堆叠到全局环境:

    (function(df){
        m   <- df[-1]
        IND <- rowSums(m) > 0
        m[] <- NA
        m[cbind(which(IND),max.col(df[-1])[IND])] <- 1
        cbind(df[1],m)
    })(df)   # run this n-times
    

    【讨论】:

    • 这很好用,但关键是要以这样一种方式进行子集化,即我为每列获得相等数量的选定观察值,或者至少尽可能相等(在我的示例中不是可能,因为有 11 个 obs 和 4 年)。我什至不知道我问的是否可能
    • @VirginiaMoreraPujol 第 12 列是由于我修改了数据。我测试了不存在1 的情况。我不明白你最后的话。此解决方案应为您提供所需的结果。如果不是,请提供失败的数据和输出。
    • 我不能将输出放在评论中,但是使用与我的示例完全相同的数据,并在其上运行您的代码,我得到的输出中只有 1 个选定的 Year1 观察值,1 个第 2 年,第 3 年为 4,第 4 年为 5。至少是第一次。每次运行它都会得到不同的输出,但它不能保证每年都有相似的 1-
    • 您可以编辑和更新您的帖子。确保第一次运行代码时 df 是“新鲜的”:
    猜你喜欢
    • 2021-04-01
    • 1970-01-01
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-28
    相关资源
    最近更新 更多