如何将一个数据框列中的 N 个值交换为任何 N 值和任何 n(行)?

How do I swap N values in one data frame column for any value of N and any n(row)?

我有包含年龄差异列 (AgeDiff) 的数据框。数据框如下所示:

library("dplyr")

test <- data.frame("Age1"=c(42, 48, 58, 25, 53, 55, 32, 58, 71, 24, 48, 48, 64, 55, 45, 55, 34, 33, 51, 22), 
                   "Age2"=c(8, 2, 1, 16, 14, 1, 11, 14, 0, 5, 2, 10, 16, 13, 3, 4, 8, 13, 8, 5)) 
test <- test %>%
mutate(AgeDiff = Age1 - Age2)

对于我正在编写的函数,用户可以输入一个拒绝最小差异and/or一个拒绝最大差异。任何小于任一阈值 than/larger 的年龄差异都会产生“超出范围”的年龄差异。

在我所做的工作中,任何小于 18 岁或大于 54 岁的年龄差异都“超出范围”。巧合的是(我对两组年龄都使用了随机数生成器),刚好有两个年龄差太小,两个年龄差太大。

我可以找到“太年轻”或“太老”的最大值,例如通过比较

TooYoung <- test %>%
filter(AgeDiff < 18) %>%
summarise(Count = n()) %>%
pull(Count)

TooOld <- test %>%
filter(AgeDiff > 54) %>%
summarise(Count = n()) %>%
pull(Count)

然后计算出哪个值更大。 TooYoungTooOld 中较大的一个给出了我需要在 test 数据中交换的行数。

我可以(预先)对 test 数据框进行排序,以便 AgeDiff 的值递增:

test <- test %>%
arrange(AgeDiff)

这给了我想要的顺序,其中数据框从最极端 AgeDiff 最小值到最极端 AgeDiff 最大值排序。现在我想要做的是将最顶部和最底部的 Age1 值交换为 TooYoungTooOld 的最大值。因为在这个例子中我有 2 个作为我的最大“超出范围”minima/maxima 计数,所以我需要交换:

交换是否导致“超出范围”并不重要AgeDiff

交换的数据帧可以是任意长度。要进行的交换次数可以是任何数字,包括 0。所以问题就变成了,对于任何交换值 >0,

AgeDiff 将在交换后重新计算。我的数据框中还有其他变量,例如 Sex,因此仅交换 Age1 值很关键。

行的重新排序并不重要。唯一的要求是解决方案交换正确的 Age1 值对。

我搜索过类似的问题,但发现的问题却大相径庭。其他问题是, , , swap of two randomly selected rows, value swaps based on grouping variables。在我的问题中,交换的数量肯定会计算出来,但人口之间的数量不同,要交换的 Age1 值会有所不同,要交换的 Age1 值的数量必须恰好是最大值“超出范围”计数,并且没有分组变量。

编辑添加:假设您有我的数据并已完成安排,您会看到第 1 行如下所示:

Age1    Age2    AgeDiff
25      16      9

第 20 行看起来像:

Age1    Age2    AgeDiff
71      0       71

Post 交换这两行将是: 第 1 行:

Age1    Age2    AgeDiff
71      16      9

第 20 行:

Age1    Age2    AgeDiff
25      0       71

所以只有两个 Age1 值被交换了。

然后第 2 行和第 19 行交换,以

结束

第 2 行

Age1    Age2    AgeDiff
58      5       17

第 19 行

Age1    Age2    AgeDiff
22      1       57

AgeDiff 列被忽略,因为它在完成交换后重新计算。

(我也错过了初始数据框也应该被称为测试,我现在已经修复了。)

我确定有更简洁的方法可以做到这一点,但是....

library("dplyr")
test <- data.frame("Age1"=c(42, 48, 58, 25, 53, 55, 32, 58, 71, 24, 48, 48, 64, 55, 45, 55, 34, 33, 51, 22), 
                   "Age2"=c(8, 2, 1, 16, 14, 1, 11, 14, 0, 5, 2, 10, 16, 13, 3, 4, 8, 13, 8, 5)) 
test <- test %>%
  mutate(AgeDiff = Age1 - Age2) %>% 
  arrange(AgeDiff) %>% 
  dplyr::mutate(row_no = row_number())
test

swap <- function(df) {
  TooYoung <- df %>%
    filter(AgeDiff < 18) %>%
    summarise(Count = n()) %>%
    pull(Count)
  
  TooOld <- df %>%
    filter(AgeDiff > 54) %>%
    summarise(Count = n()) %>%
    pull(Count)
  
  top_bottom <- max(TooYoung, TooOld)
  
  df2 <- df %>% 
    filter(row_number() > max(row_number()) - top_bottom | row_number() <= top_bottom) %>% 
    mutate(final_age1 = Age1) %>% 
    dplyr::select(final_age1, row_no)
  df2$row_no <- sort(df2$row_no, decreasing = T)
  
  df_final <- df %>% 
    left_join(df2) %>% 
    mutate(final_age1 = ifelse(is.na(final_age1), Age1, final_age1)) %>% 
    dplyr::select(-Age1, -row_no)
             
  df_final
}
swap(test)

我认为哪个能满足您的需求?

# Joining, by = "row_no"
#    Age2 AgeDiff final_age1
# 1    16       9         71
# 2     5      17         58
# 3     5      19         24
# 4    13      20         33
# 5    11      21         32
# 6     8      26         34
# 7     8      34         42
# 8    10      38         48
# 9    14      39         53
# 10   13      42         55
# 11    3      42         45
# 12    8      43         51
# 13   14      44         58
# 14    2      46         48
# 15    2      46         48
# 16   16      48         64
# 17    4      51         55
# 18    1      54         55
# 19    1      57         22
# 20    0      71         25