当 df 的目标单元格中的值不为 NA 时,如何组合 df 的行和列名称

问题描述 投票:0回答:4

我有一个这样的df:

df1 <- data.frame(c(3,NA,5), c(NA, NA, 3), c(3, 2, NA))
names(df1) <- c('number1', 'number2', 'number3')
rownames(df1) <- c('siteA', 'siteB', 'siteC')

我想创建一个新的 df,其中行和列名称由非 NA 的值组合而成。结果 df 应如下所示:

df2 <- data.frame('number' = c('number1', 'number1', 'number2', 'number3', 'number3'), 
                  'site' = c('siteA', 'siteC', 'siteC', 'siteA', 'siteB'))

如何尽可能高效地做到这一点? (我真正的 df 很大)

r dataframe filter dataset data-cleaning
4个回答
3
投票

在矩阵或表格中,每个轴都可以有一个名称,该名称是标记该轴的字符串。将 df1 转换为矩阵 m 并将它们相加。

然后使用

as.data.frame.table
将其转换为长格式,并删除带有 NA 的行。
as.data.frame.table
将添加名为
Freq
的第三列,我们不需要它,因此仅选择前 2 列,并且由于问题将数字首先颠倒了提取的两列的顺序。

 m <- as.matrix(df1)
 names(dimnames(m)) <- c("site", "number")

 m |>
  as.data.frame.table() |>
  na.omit() |>
  subset(select = 2:1)
##    number  site
## 1 number1 siteA
## 3 number1 siteC
## 6 number2 siteC
## 7 number3 siteA
## 8 number3 siteB

m
看起来像这样。请注意暗名称。

m
##        number
## site    number1 number2 number3
##   siteA       3      NA       3
##   siteB      NA      NA       2
##   siteC       5       3      NA

2
投票

使用

tidyverse
的简单方法:

df1 %>% 
    rownames_to_column('site') %>%
    pivot_longer(-site, names_to = 'number', values_to = 'value') %>%
    filter(!is.na(value)) %>%
    select(-value)

1
投票

您也可以在此处使用

which()
。首先,您找到 data.frame 中没有缺失行的位置(不需要先将其转换为矩阵)并使用
arr.ind = TRUE
获取索引。这些索引是具有非 NA 值的数据的行和列。这样做的另一个好处是 row.names 将是原始
data.frame
的 row.names 。我将矩阵输出转换为具有正确变量名称的 data.frame,然后将列名称和行名称中的值分配给数字。

df2 <- which(!is.na(df1), arr.ind = TRUE)

df2 <- data.frame(number =colnames(df1)[df2[,2]] , site = row.names(df2))

乐趣基准:

transform_fn <- function(){
  m <- as.matrix(df1)
  names(dimnames(m)) <- c("site", "number")
  
  m |>
    t() |>
    as.data.frame.table() |>
    na.omit() |>
    subset(select = 1:2)
}

which_fn <- function(){
df2 <- which(!is.na(df1), arr.ind = TRUE)

 data.frame(number =colnames(df1)[df2[,2]] , site = row.names(df2))
}

tidyverse_fn <- function(){df1 %>% 
  rownames_to_column('site') %>%
  pivot_longer(-site, names_to = 'number', values_to = 'value') %>%
  filter(!is.na(value)) %>%
  select(-value)
}

microbenchmark::microbenchmark(which_fn(),transform_fn(),tidyverse_fn(),
                               times = 1000)


1
投票

我们可以尝试

expand.grid
+
dimnames

> rev(expand.grid(dimnames(df1)))[!is.na(unlist(df1)), ]
     Var2  Var1
1 number1 siteA
3 number1 siteC
6 number2 siteC
7 number3 siteA
8 number3 siteB
© www.soinside.com 2019 - 2024. All rights reserved.