7

考虑任何数据框

            col1   col2    col3   col4
row.name11    A     23      x       y
row.name12    A     29      x       y
row.name13    B     17      x       y
row.name14    A     77      x       y

我有一个要从此数据框返回的行名列表。假设我在列表中有 row.name12 和 row.name13。我可以轻松地从数据框中返回这些行。但我也想返回上面的 4 行和下面的 4 行。这意味着我想从 row.name8 返回到 row.name17。我认为它类似于grep -A -Bshell。

可能的解决方案 - 有没有办法按行名返回行号?因为如果我有行号,我可以轻松地减去 4 并在行号中添加 4 并返回行。

注意:这里的行名只是示例。行名可以是红色、蓝色、黑色等。

4

5 回答 5

10

试试看:

extract.with.context <- function(x, rows, after = 0, before = 0) {

  match.idx  <- which(rownames(x) %in% rows)
  span       <- seq(from = -before, to = after)
  extend.idx <- c(outer(match.idx, span, `+`))
  extend.idx <- Filter(function(i) i > 0 & i <= nrow(x), extend.idx)
  extend.idx <- sort(unique(extend.idx))

  return(x[extend.idx, , drop = FALSE])
}

dat <- data.frame(x = 1:26, row.names = letters)
extract.with.context(dat, c("a", "b", "j", "y"), after = 3, before = 1)
#    x
# a  1
# b  2
# c  3
# d  4
# e  5
# i  9
# j 10
# k 11
# l 12
# m 13
# x 24
# y 25
# z 26
于 2012-10-31T10:59:21.473 回答
6

也许which()和的组合%in%可以帮助您:

dat[which(rownames(dat) %in% c("row.name13")) + c(-1, 1), ]
#            col1 col2 col3 col4
# row.name12    A   29    x    y
# row.name14    A   77    x    y

在上面,我们试图识别“dat”中的哪些行名是“row.name13”(使用which()),并+ c(-1, 1)告诉 R 返回之前的行和之后的行。如果您想包含该行,您可以执行类似+ c(-1:1).

要获取行的范围,请将逗号切换为冒号:

dat[which(rownames(dat) %in% c("row.name13")) + c(-1:1), ]
#            col1 col2 col3 col4
# row.name12    A   29    x    y
# row.name13    B   17    x    y
# row.name14    A   77    x    y

更新

匹配一个列表有点棘手,但不用考虑太多,这里有一种可能性:

myRows <- c("row.name12", "row.name13")
rowRanges <- lapply(which(rownames(dat) %in% myRows), function(x) x + c(-1:1))
# [[1]]
# [1] 1 2 3
# 
# [[2]]
# [1] 2 3 4
#
lapply(rowRanges, function(x) dat[x, ])
# [[1]]
#            col1 col2 col3 col4
# row.name11    A   23    x    y
# row.name12    A   29    x    y
# row.name13    B   17    x    y
# 
# [[2]]
#            col1 col2 col3 col4
# row.name12    A   29    x    y
# row.name13    B   17    x    y
# row.name14    A   77    x    y

这输出 a listof data.frames 可能很方便,因为您可能有重复的行(如本例中所示)。

更新 2:使用grep是否更合适

这是您问题的一种变体,使用which()...%in%方法解决起来不太方便。

set.seed(1)
dat1 <- data.frame(ID = 1:25, V1 = sample(100, 25, replace = TRUE))
rownames(dat1) <- paste("rowname", sample(apply(combn(LETTERS[1:4], 2), 
                                               2, paste, collapse = ""), 
                                         25, replace = TRUE), 
                       sprintf("%02d", 1:25), sep = ".")
head(dat1)
#               ID V1
# rowname.AD.01  1 27
# rowname.AB.02  2 38
# rowname.AD.03  3 58
# rowname.CD.04  4 91
# rowname.AD.05  5 21
# rowname.AD.06  6 90

现在,假设您想用ABand标识行AC,但您没有数字后缀的列表。

这是一个可以在这种情况下使用的小功能。它从@Spacedman 借了一点,以确保返回的行在数据范围内(根据@flodel 的建议)。

getMyRows <- function(data, matches, range) {
  rowMatches = lapply(unlist(lapply(matches, function(x)
    grep(x, rownames(data)))), function(y) y + range)
  rowMatches = lapply(rowMatches, function(x) x[x > 0 & x <= nrow(data)])
  lapply(rowMatches, function(x) data[x, ])
}

您可以按如下方式使用它(但我不会在这里打印结果)。首先,指定数据集,然后是要匹配的模式,然后是范围(在本例中,前三行和后四行)。

getMyRows(dat1, c("AB", "AC"), -3:4)

将其应用于前面的匹配row.name12and示例row.name13,您可以按如下方式使用它:getMyRows(dat, c(12, 13), -1:1).

您还可以修改该函数以使其更通用(例如,指定与列名而不是行名匹配)。

于 2012-10-31T10:17:06.897 回答
3

创建一些示例数据:

> dat=data.frame(col1=letters,col2=sample(26),col3=sample(letters))
> dat
   col1 col2 col3
1     a   26    x
2     b   12    i
3     c   15    v
...

设置我们的目标向量(注意我选择了边缘情况和重叠情况),并找到匹配的行:

> target=c("a","e","g","s")
> match = which(dat$col1 %in% target)

创建从 -2 到 +2 的匹配序列(根据您的需要进行调整)并合并:

> getThese = unique(as.vector(mapply(seq,match-2,match+2)))
> getThese
 [1] -1  0  1  2  3  4  5  6  7  8  9 17 18 19 20 21

修复边缘情况:

> getThese = getThese[getThese > 0 & getThese <= nrow(dat)]
> dat[getThese,]
   col1 col2 col3
1     a   26    x
2     b   12    i
3     c   15    v
4     d   22    d
5     e    2    j
6     f    9    l
7     g    1    w
8     h   21    n
9     i   17    p
17    q   18    a
18    r   10    m
19    s   24    o
20    t   13    e
21    u    3    k
> 

请记住,我们的目标是 a、e、g 和 s。你现在已经得到了这些加上上面的两行和下面的两行,没有重复。

如果您使用的是行名,只需从中创建“匹配”。我正在使用一个列。

如果这是我的问题,我会使用 testthat 包编写更多测试。

于 2012-10-31T11:16:37.120 回答
1

另一种选择是使用filter. 万一stats::filter被掩盖,例如dplyr::filter您必须使用stats::filter.

dat <- data.frame(x = seq_along(letters), row.names = letters)

i <- rownames(dat) %in% c("a", "b", "j", "y") #Get the matches

nAfter <- 3
nBefore <- 1

fi <- seq(-nBefore, nAfter)
n <- max(abs(x))
fi <- seq(-n, n) %in% fi

dat[head(tail(filter(c(rep(FALSE, n), i, rep(FALSE, n)), fi), -n), -n) > 0,, drop = FALSE]
#   x
#a  1
#b  2
#c  3
#d  4
#e  5
#i  9
#j 10
#k 11
#l 12
#m 13
#x 24
#y 25
#z 26
于 2021-08-18T07:01:03.857 回答
0

我将简单地进行如下操作:

dat[(grep("row.name12",row.names(dat))-4):(grep("row.name13",row.names(dat))+4),]

grep("row.name12",row.names(dat))给你"row.name12"作为名字的行号,所以

(grep("row.name12",row.names(dat))-4):(grep("row.name13",row.names(dat))+4)

为您提供一系列行号,范围从 named 行之前的第"row.name12"4 行到 named 行之后的第 4 行"row.name13"

于 2012-10-31T10:19:52.273 回答