【问题标题】:Indexing Pandas data frames: integer rows, named columns索引 Pandas 数据帧:整数行、命名列
【发布时间】:2015-04-29 13:08:03
【问题描述】:

df 是一个熊猫数据框。

  • df.loc[] 只接受名字
  • df.iloc[] 只接受整数(实际位置)
  • df.ix[] 接受名称和整数:

当引用行时,df.ix[row_idx, ] 只想被命名。例如

df = pd.DataFrame({'a' : ['one', 'two', 'three','four', 'five', 'six'],
                   '1' : np.arange(6)})
df = df.ix[2:6]
print(df)

   1      a
2  2  three
3  3   four
4  4   five
5  5    six

df.ix[0, 'a']

抛出一个错误,它不会返回“二”。

当引用列时,iloc 更喜欢整数,而不是名称。例如

df.ix[2, 1]

返回“三”,而不是 2。(尽管 df.idx[2, '1'] 确实返回 2)。

奇怪的是,我想要完全相反的功能。通常我的列名非常有意义,所以在我的代码中我直接引用它们。但是由于大量的观察清理,我的 pandas 数据框中的行名通常不对应于range(len(df))

我意识到我可以使用:

df.iloc[0].loc['a'] # returns three

但它看起来很丑!有谁知道这样做的更好方法,使代码看起来像这样?

df.foo[0, 'a'] # returns three

事实上,是否可以将我自己的新方法添加到pandas.core.frame.DataFrames,例如 df.idx(rows, cols)其实就是df.iloc[rows].loc[cols]

【问题讨论】:

标签: python pandas dataframe


【解决方案1】:

这是一个迟到的答案,但@unutbu 的评论仍然有效并且是解决这个问题的好方法。

用整数行和命名列(标记列)索引 DataFrame:

df.loc[df.index[#], 'NAME'] 其中# 是有效的整数索引,NAME 是列的名称。

【讨论】:

  • 在长数据帧上似乎很慢。
  • 但是效果很好。我昨天偶然发现了这个,这是我更新数据帧副本所需的确切语法,通过索引和列名链接回原始数据。
  • 您的方法要求索引中的值是唯一的。否则它将返回一个带有所有匹配索引“#”的系列
【解决方案2】:

我们可以重置索引,然后像这样使用基于 0 的索引

df.reset_index(drop=True).loc[0,'a']

编辑:从列名索引'a' 中删除了[],因此它只输出值

【讨论】:

  • 这不会返回有效结果,因为索引中没有“0”。
  • 现在明白这个问题了,谢谢!请看看编辑后的代码是否看起来足够干净......
  • @KrishnaBandhakavi ,但是,如果您从 'a' 中删除 [],它将更准确地返回。 => df.reset_index().loc[0,'a']
  • 这是在非唯一索引的情况下进行分配的唯一答案。不过,在这种情况下,您需要保留原始索引,然后再将其放回原处。
【解决方案3】:

像 df["a"][0] 这样的东西对我来说很好用。你可以试试看!

【讨论】:

  • 如果您解释一下为什么这对您有用以及为什么它对作者有用,那将是一个更好的答案
【解决方案4】:

要通过行/列标签在DataFrame 中获取或设置单个 值,您最好使用DataFrame.at 而不是DataFrame.loc,因为它是...

  1. 更快
  2. 您更明确地表示只想访问单个值。

其他人已经展示了,如果您从行的整数位置开始,您仍然必须首先使用DataFrame.index 找到行标签,因为DataFrame.at 只接受标签:

df.at[df.index[0], 'a']
# Out: 'three'

基准测试:

%timeit df.at[df.index[0], 'a']
# 7.57 µs ± 30.8 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit df.loc[df.index[0], 'a']
# 10.9 µs ± 53.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit df.iloc[0, df.columns.get_loc("a")]
# 13.3 µs ± 24 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

为了完整性:

DataFrame.iat 用于按整数位置访问行/列对的单个值。

【讨论】:

  • DataFrame 有多大?对于不仅仅是有序整数的索引,我假设df.index 需要进行反向查找,这可能需要O(n) 迭代n 行。它将如何处理重复项? iatO(1) 不是所有解决方案中最快的吗?
  • @MateenUlhaq 必须与 OP 给出的 df 示例相同。 df.index 是散列的,所以 O(1)。重复项不会被忽略,因此请务必确保您之前已过滤重复项。我不记得iat 的时间安排,但一般来说,位置查找并不总是一种选择。
【解决方案5】:

现有的答案对我来说似乎是短视的。

有问题的解决方案

  1. df.loc[df.index[0], 'a']
    这里的策略是获取第 0 行的行标签,然后照常使用.loc。我看到了两个问题。

    1. 如果 df 有重复的行标签,df.loc[df.index[0], 'a'] 可能会返回多行。
    2. .loc.iloc 慢,所以你在这里牺牲了速度。
  2. df.reset_index(drop=True).loc[0, 'a']
    这里的策略是重置索引,使行标签变为 0, 1, 2, ... 因此.loc[0] 给出与.iloc[0] 相同的结果。不过,这里的问题是运行时间,因为.loc.iloc 慢,并且您将承担重置索引的成本。

更好的解决方案

我建议关注@Landmaster's comment:

df.iloc[0, df.columns.get_loc("a")]

本质上,这与df.iloc[0, 0] 相同,只是我们使用df.columns.get_loc("a") 动态获取列索引。

要索引多个列,例如['a', 'b', 'c'],请使用:

df.iloc[0, [df.columns.get_loc(c) for c in ['a', 'b', 'c']]]

更新

这在here 中作为我的course on Pandas 的一部分进行了讨论。

【讨论】:

  • 您的首选解决方案df.iloc[0, df.columns.get_loc("a")] 不能免除重复标签,因为列标签也可以重复。所以你什么也得不到,但它比df.loc[df.index[0], 'a'] 更冗长、更慢。对于单值访问,无论如何都不应该使用它们。
  • @Darkonaut 重复的列名比重复的行标签发生的可能性要小得多。此外,df.iloc[0, df.columns.get_loc("a")]df.loc[df.index[0], 'a'] 在运行时应该几乎相同,除非 df 有数千列,但即便如此,差异也应该是微不足道的。
【解决方案6】:

一个很晚的答案,但让我感到惊讶的是,这些年来 pandas 仍然没有这样的功能。如果它让你很恼火,你可以将自定义索引器修补到 DataFrame 中:

class XLocIndexer:
    def __init__(self, frame):
        self.frame = frame
    
    def __getitem__(self, key):
        row, col = key
        return self.frame.iloc[row][col]

pd.core.indexing.IndexingMixin.xloc = property(lambda frame: XLocIndexer(frame))

# Usage
df.xloc[0, 'a'] # one

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 2019-08-12
    • 1970-01-01
    • 1970-01-01
    • 2017-02-06
    • 2019-07-22
    • 1970-01-01
    相关资源
    最近更新 更多