对唯一顺序索引的多索引熊猫数据框进行切片和赋值
Posted
技术标签:
【中文标题】对唯一顺序索引的多索引熊猫数据框进行切片和赋值【英文标题】:Slicing and assigning values multi-indexed pandas dataframe of unique sequential indices 【发布时间】:2017-05-10 19:27:15 【问题描述】:我想选择并更改数据框单元格的值。该数据框使用了 2 个索引:“datetime”和“idx”。两者都包含唯一且连续的标签。 'datetime' 索引具有 datetime 类型的日期时间标签,而 'idx' 具有整数值标签。
import numpy as np
import pandas as pd
dt = pd.date_range("2010-10-01 00:00:00", periods=5, freq='H')
d = 'datetime': dt, 'a': np.arange(len(dt))-1,'b':np.arange(len(dt))+1
df = pd.DataFrame(data=d)
df.set_index(keys='datetime',inplace=True,drop=True)
df.sort_index(axis=0,level='datetime',ascending=False,inplace=True)
df.loc[:,'idx'] = np.arange(0, len(df),1)+5
df.set_index('idx',drop=True,inplace=True,append=True)
print(df)
'这是数据框:
a b
datetime idx
2010-10-01 04:00:00 5 3 5
2010-10-01 03:00:00 6 2 4
2010-10-01 02:00:00 7 1 3
2010-10-01 01:00:00 8 0 2
2010-10-01 00:00:00 9 -1 1
'假设我想获取 idx=5 的行。我怎么做?我可以用这个:
print(df.iloc[0])
然后我会得到下面的结果:
a 3
b 5
Name: (2010-10-01 04:00:00, 5), dtype: int32
但我想访问并设置该单元格中的值,其中 idx=5,column='a',通过指定 idx 值和列名 'a'。我该怎么做?
请指教。
【问题讨论】:
如果您的问题得到解决,请考虑接受 MaxU 的回答。它更详细。 好的,我接受了 MaxU 的回答。但是,我认为我会采用您的解决方案,因为我测试它更快(1.28 毫秒对 2.77 毫秒),并且更短,无需将整数转换为字符串,然后与 'idx' 字符串连接以输入到 eval 函数中。 ..另外,因为我将在 for 循环中使用它。 是的。因为 xs 不会遍历整个列来搜索 idx 值。它将计算 5 的哈希值并在 1 go 中获取它。另一个索引,即日期时间索引太长,无法在切片中写入:| @ConanG,你为什么要在 for 循环中使用它?您确定不能使用矢量化方法吗? @ConanG 在我的回答下方阅读 piRSquared 的评论。如果idx
不是唯一的,则不能使用set_value
。
【参考方案1】:
您可以使用DatFrame.query() 方法查询MultiIndex DF:
In [54]: df
Out[54]:
a b
datetime idx
2010-10-01 04:00:00 5 3 5
2010-10-01 03:00:00 6 2 4
2010-10-01 02:00:00 7 1 3
2010-10-01 01:00:00 8 0 2
2010-10-01 00:00:00 9 -1 1
In [55]: df.query('idx==5')
Out[55]:
a b
datetime idx
2010-10-01 04:00:00 5 3 5
In [56]: df.query('idx==5')['a']
Out[56]:
datetime idx
2010-10-01 04:00:00 5 3
Name: a, dtype: int32
如果您需要设置/更新某些单元格,也可以使用DataFrame.eval() 方法:
In [61]: df.loc[df.eval('idx==5'), 'a'] = 100
In [62]: df
Out[62]:
a b
datetime idx
2010-10-01 04:00:00 5 100 5
2010-10-01 03:00:00 6 2 4
2010-10-01 02:00:00 7 1 3
2010-10-01 01:00:00 8 0 2
2010-10-01 00:00:00 9 -1 1
解释:
In [59]: df.eval('idx==5')
Out[59]:
datetime idx
2010-10-01 04:00:00 5 True
2010-10-01 03:00:00 6 False
2010-10-01 02:00:00 7 False
2010-10-01 01:00:00 8 False
2010-10-01 00:00:00 9 False
dtype: bool
In [60]: df.loc[df.eval('idx==5')]
Out[60]:
a b
datetime idx
2010-10-01 04:00:00 5 3 5
PS 如果您原来的 MultiIndex 没有名称,您可以使用 rename_axis() 方法轻松设置它们:
df.rename_axis(('datetime','idx')).query(...)
替代(稍微贵一点)解决方案 - 使用 sort_index()
+ pd.IndexSlice[]
:
In [106]: df.loc[pd.IndexSlice[:,5], ['a']]
...
skipped
...
KeyError: 'MultiIndex Slicing requires the index to be fully lexsorted tuple len (2), lexsort depth (0)'
所以我们需要先对索引进行排序:
In [107]: df.sort_index().loc[pd.IndexSlice[:,5], ['a']]
Out[107]:
a
datetime idx
2010-10-01 04:00:00 5 3
【讨论】:
他需要在查询后更改a
列的值。
@MYGz,谢谢你的提示!我已经扩展了我的答案【参考方案2】:
另一种方法。
选择值:
df.xs(5, level=-1)
设定值:
df.set_value(df.xs(5, level=-1).index, 'a', 100)
【讨论】:
仅当5
在MultiIndex
中是唯一的时才有效
@piRSquared 嗯。你说的对。在这种情况下,他将不得不使用这两个密钥。在这种情况下,即使 eval 或任何其他方法也不起作用。对吗?
这是set_value
的限制。 set_value
非常快,但仅适用于单个单元格。如果5
不是唯一的,那么df.xs(5, level=-1).index
将不是标量。使用loc
分配没有这个问题,但速度较慢。【参考方案3】:
如果要在大型数据集中循环使用,我意识到先将数据框的列提取为 pandas Series 类型,然后继续进行选择和分配操作,大约快 20 倍。
或者
如果索引标签碰巧是连续整数,则对 numpy 数组甚至更快(几乎快 10000 倍)。
MYGz 的解决方案很好,但在我的 for 循环用例中,由于这些操作花费了大部分时间,因此速度太慢而无法实现。
【讨论】:
以上是关于对唯一顺序索引的多索引熊猫数据框进行切片和赋值的主要内容,如果未能解决你的问题,请参考以下文章