使用 R 中另一列的条件交换列中的值

Swapping the values in columns with the condition of another column in R

对于以下数据,如果p.2值大于0.5,我想交换第1列(b.1.1)和第2列b.1.2的值, 第 3 列(b.2.1) 和第 4 列 b.2.2 、第 5 列(b.3.1) 和第 6 列 b.3.2

 mydata
            b.1.1       b.1.2      b.2.1      b.2.2     b.3.1      b.3.2        p.1       p.2
    1  0.40772028  0.43064446  0.2697412  0.9191535 0.1523922  0.7629324 0.86061981 0.1393802
    2 -0.77459375  0.79860856 -0.5263932 -0.5640303 0.5131236  0.6472614 0.63494425 0.3650557
    3 -0.06088828  0.42685669 -1.0643744  0.8330836 0.1184059  0.6661079 0.07382585 0.9261742
    4  1.54204242 -0.08987067 -0.7365012  0.3762336 0.3781115 -0.7340340 0.65481949 0.3451805
    5 -0.73397310  1.34927693  0.2202689  0.2422944 1.5267535 -0.5207967 0.54425551 0.4557445

例如,在第一行中,p.1p.2之间,p.2不大于0.5,我不交换这一行的任何值。在第三行,p.2 大于 0.5,所以,我想交换上面写的每个 beta 对的行值。 感谢您的帮助。

这是一个选项。创建一个索引以对以 'b' ('i1') 开头的列进行子集化,并创建另一个索引对行进行子集化 ('i2')。然后,split将数据集分成list个数据集,根据行子集后的列名相似度('i2'),循环listrev删除元素,cbind data.frames 的 list 并将其更新为原始数据集 rows/columns

i1 <-  startsWith(names(mydata), "b")
i2 <- mydata$p.2 > 0.5
mydata[i2, i1] <- do.call(cbind, 
            lapply(split.default(mydata[i2, i1, drop = FALSE],
         sub("\.\d+$", "", names(mydata)[i1])), rev))
mydata
#       b.1.1       b.1.2      b.2.1      b.2.2     b.3.1      b.3.2        p.1       p.2
#1  0.4077203  0.43064446  0.2697412  0.9191535 0.1523922  0.7629324 0.86061981 0.1393802
#2 -0.7745937  0.79860856 -0.5263932 -0.5640303 0.5131236  0.6472614 0.63494425 0.3650557
#3  0.4268567 -0.06088828  0.8330836 -1.0643744 0.6661079  0.1184059 0.07382585 0.9261742
#4  1.5420424 -0.08987067 -0.7365012  0.3762336 0.3781115 -0.7340340 0.65481949 0.3451805
#5 -0.7339731  1.34927693  0.2202689  0.2422944 1.5267535 -0.5207967 0.54425551 0.4557445

另一种选择是 tidyverse,我们将其转换为 'long' 格式,以该格式进行转换,然后重塑回 'wide' 格式

library(dplyr)
library(tidyr)
library(stringr)
library(tibble)
mydata %>% 
   rownames_to_column('rn') %>%
   pivot_longer(cols = -c(rn, p.1, p.2)) %>%
   group_by(rn, grp = str_remove(name, "\.\d+$")) %>% 
   mutate(value = case_when(p.2 > 0.5 ~ rev(value), TRUE ~ value)) %>% 
   ungroup %>% 
   select(-grp) %>% 
   pivot_wider(names_from = name, values_from = value) %>%
   select(names(mydata)) 
# A tibble: 5 x 8
#   b.1.1   b.1.2  b.2.1  b.2.2 b.3.1  b.3.2    p.1   p.2
#   <dbl>   <dbl>  <dbl>  <dbl> <dbl>  <dbl>  <dbl> <dbl>
#1  0.408  0.431   0.270  0.919 0.152  0.763 0.861  0.139
#2 -0.775  0.799  -0.526 -0.564 0.513  0.647 0.635  0.365
#3  0.427 -0.0609  0.833 -1.06  0.666  0.118 0.0738 0.926
#4  1.54  -0.0899 -0.737  0.376 0.378 -0.734 0.655  0.345
#5 -0.734  1.35    0.220  0.242 1.53  -0.521 0.544  0.456

数据

mydata <- structure(list(b.1.1 = c(0.40772028, -0.77459375, -0.06088828, 
1.54204242, -0.7339731), b.1.2 = c(0.43064446, 0.79860856, 0.42685669, 
-0.08987067, 1.34927693), b.2.1 = c(0.2697412, -0.5263932, -1.0643744, 
-0.7365012, 0.2202689), b.2.2 = c(0.9191535, -0.5640303, 0.8330836, 
0.3762336, 0.2422944), b.3.1 = c(0.1523922, 0.5131236, 0.1184059, 
0.3781115, 1.5267535), b.3.2 = c(0.7629324, 0.6472614, 0.6661079, 
-0.734034, -0.5207967), p.1 = c(0.86061981, 0.63494425, 0.07382585, 
0.65481949, 0.54425551), p.2 = c(0.1393802, 0.3650557, 0.9261742, 
0.3451805, 0.4557445)), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5"))

另一种只使用基础而不使用正则表达式的选项是

reorderRows <- function(x, nullFrame){
  for(i in length(x)){
    if(x[i, 8] > 0.5)x = x[c(2,1,4,3,6,5,7,8)]
    nullFrame <- rbind(nullFrame, x)
  }
  return(nullFrame)
}

已申请:

dat <- data.frame(matrix(rnorm(80), ncol = 8))
colnames(dat) <- c("b.1.1", "b.1.2", "b.2.1", "b.2.2", 
                   "b.3.1", "b.3.2", "p.1", "p.2")

emptyFrame <- NULL
dat2 <- reorderRows(dat, emptyFrame)

它可能比以前的答案慢很多,但对于小数据集可能更容易修改