如何根据列名而非索引选择数据框中的列范围?

7

在创建如下的pandas数据框时:

import pandas as pd
import numpy as np

df = pd.DataFrame(np.random.randint(10, size=(6, 6)),
                  columns=['c' + str(i) for i in range(6)],
                  index=["r" + str(i) for i in range(6)])

可能看起来像下面这样:

    c0  c1  c2  c3  c4  c5
r0   2   7   3   3   2   8
r1   6   9   6   7   9   1
r2   4   0   9   8   4   2
r3   9   0   4   3   5   4
r4   7   6   8   8   0   8
r5   0   6   1   8   2   2

我可以使用.loc轻松选择特定的行和/或一系列列:

print df.loc[['r1', 'r5'], 'c1':'c4']

这将返回:

    c1  c2  c3  c4
r1   9   6   7   9
r5   6   1   8   2

那么,在列表中,我可以选择特定的行/列,使用冒号选择一系列行/列。

那么如何在R中实现呢?这里这里,人们总是必须通过他们的索引指定所需的列范围,但是无法 - 或者至少我没有找到 - 按名称访问它们。举个例子:

df <- data.frame(c1=1:6, c2=2:7, c3=3:8, c4=4:9, c5=5:10, c6=6:11)
rownames(df) <- c('r1', 'r2', 'r3', 'r4', 'r5', 'r6')

命令
df[c('r1', 'r5'),'c1':'c4']

这段代码无法正常运行并且会抛出一个错误。唯一对我有效的方法是

df[c('r1', 'r5'), 1:4]

这个函数返回

   c1 c2 c3 c4
r1  1  2  3  4
r5  5  6  7  8

但是我该如何根据列名而不是索引选择列(当我在分析过程中删除某些列时,这可能很重要)?在这种特殊情况下,我当然可以使用 grep,但对于具有任意名称的列呢?

因此,我不想使用

df[c('r1', 'r5'),c('c1','c2', 'c3', 'c4')]

但实际上是一段。

编辑:

后续问题可以在这里找到。


感觉在提交问题后编辑以包括行有点像是改变了问题的范围。最好还是撤回编辑并提出一个新的问题。虽然列名和行名在R数据框中似乎非常相关,但它们的处理方式却有很大不同。(虽然这不是我投反对票的原因 - 也不确定这是否是原因。) - Gregor Thomas
2
@Gregor: 好的,我对R的详细信息不是那么熟悉,在Pandas中很简单,我只需要做:df.loc ['r1':'r3','c1':'c4'],因此行和列并没有被区分对待。如果没有更好的答案出现,我当然会接受提供的答案(请参阅我的第一个评论)。是的,你是对的,我应该在原始问题中放置行选择,所以我理解你的负投票;谢谢解释! - Cleb
即使从一开始,我认为将其作为一个单独的问题会更好(但我仍然不会给它投反对票)。对于列,我可以想到三种好的方法:base::subset(如答案中所示)、dplyr::selectdata.table。我不知道除了黑客攻击之外是否有任何行名称的方法。但这会成为一个很好的单独问题,并可能获得一些特定的兴趣/创新解决方案。 - Gregor Thomas
@Cleb,你的回答被踩可能是因为你使用了Python和Pandas标签,而问题实际上是关于“R”的。 - Merlin
@Merlin:这是为了吸引可能知道如何在R中完成此操作的熊猫专家。在我看来,错误的标签不应该成为否决投票的奇怪原因,但是...感谢您的编辑。 - Cleb
显示剩余2条评论
5个回答

14

看起来你可以用 subset 来完成这个任务:

> df <- data.frame(c1=1:6, c2=2:7, c3=3:8, c4=4:9, c5=5:10, c6=6:11)
> rownames(df) <- c('r1', 'r2', 'r3', 'r4', 'r5', 'r6')
> subset(df, select=c1:c4)
   c1 c2 c3 c4
r1  1  2  3  4
r2  2  3  4  5
r3  3  4  5  6
r4  4  5  6  7
r5  5  6  7  8
r6  6  7  8  9
> subset(df, select=c1:c2)
   c1 c2
r1  1  2
r2  2  3
r3  3  4
r4  4  5
r5  5  6
r6  6  7

如果您想按行名称范围对子集进行筛选,可以使用以下技巧:

> gRI <- function(df, rName) {which(match(rNames, rName) == 1)}
> df[gRI(df,"r2"):gRI(df,"r4"),]
   c1 c2 c3 c4 c5 c6
r2  2  3  4  5  6  7
r3  3  4  5  6  7  8
r4  4  5  6  7  8  9

确实可以。现在如何同时选择行呢?如果您想要特定的行,则subset(df[c('r1', 'r3'),], select=c1:c4)可以工作,但是如果是一系列的行呢(请参见我的编辑)?暂时点赞,根据其他答案的质量可能会稍后接受它... - Cleb
2
我认为标准做法是不给行命名,然后使用标准索引范围来子集化行。如果你需要行名称,你可以随时将它们添加为 id 列。 - evan.oman
这可能是一个不错的解决方法。但仍然感觉奇怪,这本应该是可以实现的。 - Cleb
请参阅我的最新修改,了解一种行名称范围子集的解决方案。我看到的大多数 R 代码都使用类似于 df[beginInd:endInd,] 的方式进行行子集选择。 - evan.oman
是的,按索引进行子集操作似乎更为常见,但我仍然感到惊讶,因为没有内置该功能。 - Cleb

2
使用dplyr包的解决方案,但需要事先指定要选择的行。
rowName2Match <- c("r1", "r5")

df1 <- df %>% 
  select(matches("2"):matches("4")) %>% 
  add_rownames() %>% 
  mutate(idRow = match(rowname, rowName2Match)) %>% 
  slice(which(!is.na(idRow))) %>% 
  select(-idRow)
df1

> df1
Source: local data frame [2 x 4]

  rowname    c2    c3    c4
   <chr> <int> <int> <int>
1      r1     2     3     4
2      r5     6     7     8

感谢提供另一种解决方案,但似乎比@evan058的解决方案更加复杂。 - Cleb

2

如果您不介意使用data.table,那么一个替代subset的方法是:

data.table::setDT(df)
df[1:3, c2:c4, with=F]
   c2 c3 c4
1:  2  3  4
2:  3  4  5
3:  4  5  6

然而,这仍未解决行范围子集的问题。


好的,谢谢你提供的替代方法(已点赞)。行选择不是原来问题的一部分;我并没有想到它和列选择有那么大的不同,所以可以算是一个额外的奖励 ;) - Cleb

1

在 @evan058 的答案基础上添加:

subset(df[rownames(df) %in% c("r3", "r4", "r5"),], select=c1:c4)

c1 c2 c3 c4
r3  3  4  5  6
r4  4  5  6  7
r5  5  6  7  8

但请注意,: 运算符在这里可能不起作用;您必须明确写出要包含的每一行的名称。更容易的方法是按照其他列的特定值进行分组,或者像 @evan058 在评论中提到的那样创建一个索引列。

谢谢,但是subset(df[c('r1', 'r3'),], select=c1:c4)似乎更方便。但我实际上想避免指定行名称。无论如何,已经点赞了 ;) - Cleb

-1

这似乎太容易了,也许我做错了什么。

df <- data.frame(c1=1:6, c2=2:7, c3=3:8, c4=4:9, c5=5:10, c6=6:11,
                 row.names=c('r1', 'r2', 'r3', 'r4', 'r5', 'r6'))


df[c('r1','r2'),c('c1','c2')]

   c1 c2
r1  1  2
r2  2  3

1
那需要指定我想要避免的行和列。附注:我没有投反对票。 - Cleb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接