返回r数据框中特定行的上下行
Posted
技术标签:
【中文标题】返回r数据框中特定行的上下行【英文标题】:Returning above and below rows of specific rows in r dataframe 【发布时间】:2012-10-31 10:13:26 【问题描述】:考虑任何数据框
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。我认为它类似于 shell 中的grep -A -B
。
可能的解决方案 - 有没有办法按行名返回行号?因为如果我有行号,我可以轻松地减去 4 并在行号中添加 4 并返回行。
注意:这里的行名只是示例。行名可以是红色、蓝色、黑色等。
【问题讨论】:
【参考方案1】:试试看:
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
【讨论】:
很好地使用了Filter()
(我想这就是它的用途,对吧?)恭喜你获得了 10k!我刚看到!【参考方案2】:
也许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
这会输出data.frame
s 中的list
,这可能很方便,因为您可能有重复的行(如本例中所示)。
更新 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
现在,假设您想用AB
和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.name12
和row.name13
的示例中,可以如下使用:getMyRows(dat, c(12, 13), -1:1)
。
您还可以修改该函数以使其更通用(例如,指定与列名而不是行名匹配)。
【讨论】:
谢谢。我尝试了 2 行,但得到了一些有趣的结果。使用您的示例(只有 1 个行名)它工作正常,但是如果我想做 %in% c("row.name12", "row.name13") 那么我得到了有趣的结果。 我非常喜欢列表的想法。为了使事情变得完美,您应该添加一些东西来删除可能超出范围的行索引。 @flodel,我添加了一个可能的解决方案(基于 Spacedman 的回答),并举例说明如果grep
更有可能成为匹配的候选者,如何解决该问题。
【参考方案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 包编写更多测试。
【讨论】:
我总是忘记mapply
。我只是将您的 getThese
更改为 getThese <= nrow(dat)
以可能也包括最后一行。
糟糕。因一个错误而关闭。如果我写了更多的测试用例......将编辑。【参考方案4】:
另一种选择是使用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
【讨论】:
【参考方案5】:我会简单地按照以下方式进行:
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)
为您提供一系列行号,范围从名为 "row.name12"
的行之前的第 4 行到名为 "row.name13"
的行之后的第 4 行。
【讨论】:
以上是关于返回r数据框中特定行的上下行的主要内容,如果未能解决你的问题,请参考以下文章