如何引用 Pandas 数据框的索引?

14

我有一个Pandas数据框,其中我已将某些列指定为索引:

planets_dataframe.set_index(['host','name'], inplace=True)

我希望能够在各种情境下引用这些索引。在查询中使用索引的名称是可行的。

planets_dataframe.query('host == "PSR 1257 12"')

但是如果尝试使用它来获取索引值的列表,就会导致错误,而在它是列时我可以这样做。

planets_dataframe.name
#AttributeError: 'DataFrame' object has no attribute 'name'

或者当它是一个“常规”列时,将其用于列出结果

planets_dataframe.query('30 > mass > 20 and discoveryyear > 2009')['name']
#KeyError: u'no item named name'

我如何引用我正在使用作为索引的数据框架的“列”?


set_index 之前:

planets_dataframe.columns
# Index([u'name', u'lastupdate', u'temperature', u'semimajoraxis', u'discoveryyear', u'calculated', u'period', u'age', u'mass', u'host', u'verification', u'transittime', u'eccentricity', u'radius', u'discoverymethod', u'inclination'], dtype='object')

set_index之后:

planets_dataframe.columns
#Index([u'lastupdate', u'temperature', u'semimajoraxis', u'discoveryyear', u'calculated', u'period', u'age', u'mass', u'verification', u'transittime', u'eccentricity', u'radius', u'discoverymethod', u'inclination'], dtype='object')
2个回答

20

我认为你对索引的理解有些误解。你不能仅仅“指定”某些列作为索引,也就是说,你不能只是给特定的列打上标签,告诉它们“这是一个索引”。索引是一种独立的数据结构,可以包含甚至不存在于列中的数据。如果你使用set_index函数,你会将那些列“移动”到索引中,所以它们不再是普通列。这就是为什么你不能再以前那样使用它们了:因为它们已经不存在了。

你可以在使用set_index函数时传递drop=False参数,告诉它保留那些列作为列而不是将它们放入索引中(实际上是将它们复制到索引中而不是移动它们),例如:df.set_index('SomeColumn', drop=False)。但是,你应该知道索引和列仍然是不同的,例如,如果你修改列的值,这不会影响存储在索引中的内容。

总之,索引并不是DataFrame的列,因此,如果你想要将某些数据同时作为索引和列使用,你需要在两个位置都复制它们。关于这个问题有一些讨论可以在这里找到。


在这种情况下,有什么最佳实践的想法吗?使用 drop=False 应该可以解决问题;但会失去索引提供的一些语义(特别是其分层性质)并导致一些冗余。 - orome
@raxacoricofallapatorius: 它如何“失去一些语义”?使用 drop=False 不会改变索引的语义,它只是让您可以将列用作索引和常规列。至于冗余性,概念上可能有点烦人,但我在实践中并没有发现这是一个巨大的问题。 - BrenBarn
抱歉,两种情况混在一起了。所以保留应该没有问题,我猜这就是 drop=False 的作用吧。 - orome

7

可以使用索引的 get_level_values 方法来访问信息:

import numpy as np
import pandas as pd
np.random.seed(1)

df = pd.DataFrame(np.random.randint(4, size=(10,4)), columns=list('ABCD'))    
idf = df.set_index(list('AB'))

idf.index.get_level_values('A')大致相当于df['A']。但请注意类型和dtype的变化:

print(df['A'])
# 0    1
# 1    3
# 2    3
# 3    0
# 4    2
# 5    2
# 6    3
# 7    1
# 8    3
# 9    3
# Name: A, dtype: int32

def level(df, lvl):
    return df.index.get_level_values(lvl)

print(level(idf, 'A'))
# Int64Index([1, 3, 3, 0, 2, 2, 3, 1, 3, 3], dtype='int64')

这里,您可以使用.index.get_level_values('A')来获取相应的信息,而不是选择带有['A']的列:

print(df.query('3>C>0 and D>0')['A'])
# 8    3
# Name: A, dtype: int32

print(level(idf.query('3>C>0 and D>0'), 'A'))
# Int64Index([3], dtype='int64')

数据库设计的黄金法则之一是,“不要在两个地方重复存储同样的数据”,因为迟早会导致数据不一致,从而损坏数据。所以我建议不要将数据同时存储为列和索引,主要是因为这可能会导致数据损坏,也可能是内存使用效率低下的原因。

这是正确的,但如果您经常使用这些列,则以此方式获取级别值的冗长性很快会变得痛苦。是否复制或使用此方法可能取决于您需要以每种方式使用数据的频率。 - BrenBarn

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