Python Dataframe:为每列选择不同的索引
假设我有以下数据帧: 从导入数据帧 来自numpy import arangePython Dataframe:为每列选择不同的索引,python,pandas,Python,Pandas,假设我有以下数据帧: 从导入数据帧 来自numpy import arange lst = [ range(10), range(11,21), range(21,31) ] df = DataFrame(lst).T.set_index(arange(0.1, 1.1, 0.1)) 0 1 2 0.1 0 11 21 0.2 1 12 22 0.3 2 13 23 0.4 3 14 24 0.5 4 15 25 0.6 5 16 26 0
lst = [ range(10), range(11,21), range(21,31) ]
df = DataFrame(lst).T.set_index(arange(0.1, 1.1, 0.1))
0 1 2
0.1 0 11 21
0.2 1 12 22
0.3 2 13 23
0.4 3 14 24
0.5 4 15 25
0.6 5 16 26
0.7 6 17 27
0.8 7 18 28
0.9 8 19 29
1.0 9 20 30
我想用不同的索引范围选择每一列
例如,对于列:
- 0:我只希望索引为0.6到0.9的行
- 1:我只希望索引为0.2到0.3的行
- 2:我只希望索引为0.1到0.3的行
0 1 2
0.1 NaN NaN 21.0
0.2 NaN 12.0 22.0
0.6 5.0 NaN NaN
0.7 6.0 NaN NaN
0.8 7.0 NaN NaN
0.9 8.0 NaN NaN
import pandas as pd
lst = [ range(10), range(11,21), range(21,31) ]
df = pd.DataFrame(lst).T
dict = {0:[6,9], 1:[2,3], 2:[1,3]}
df2 = pd.DataFrame(columns = df.columns, index=df.index)
for k in dict:
df2[k] = df[k][dict[k][0]:dict[k][1]+1]
print(df2)
我目前的解决方案是:
idx = array([ [0.6, 0.9], [0.2, 0.3], [0.1, 0.3] ])
df2 = DataFrame((df[col][i[0]:i[1]] for i, col in zip(idx, df))).T
也许不是更好的解决办法
谢谢大家的回答
比较
我写了一个脚本来测试每个答案。
基准分为两部分:
:仅适用于标准索引(0,1,2,3,…)的答案STD
:与通用索引一起使用的答案GEN
from numpy import arange, array, linspace from numpy.random import rand, randint from pandas import DataFrame from timeit import Timer # yellowhat def yellowhat(df, idx): df2 = DataFrame((df[col][i[0]:i[1]] for i, col in zip(idx, df))).T return df2 # user3483203 def user3483203(df, idx): from numpy import arange r = arange(df.shape[0])[:, None] m = (idx[:,0] <= r) & (idx[:,1] > r) df2 = df.mask(~m).dropna(how='all') return df2 def user3483203_2(df, idx): from numpy import zeros, bool8, arange def foo(a, idx): out = zeros(a, dtype=bool8) for (i, j), k in zip(idx, arange(a[1])): out[i:j, k] = True return out df2 = df.mask(~foo(df.shape, idx)).dropna(how='all') return df2 def user3483203_mod(df, idx): r = df.index.values[:,None] m = (r >= idx[:,0]) & (r <= idx[:,1]) df2 = df.mask(~m).dropna(how='all') return df2 # def GeorgeLPerkins(df, idx): from pandas import DataFrame dct = {i : row for i, row in enumerate(idx)} df2 = DataFrame(columns = df.columns, index=df.index) for k in dct: df2[k] = df[k][dct[k][0] : dct[k][1]] return df2 # def piRSquared(df, idx): tups = sorted([(i, j) for j, args in enumerate(idx) for i in range(*args)]) df2 = df.stack().loc[tups].unstack() return df2 # def sacul(df, idx): from pandas import concat df2 = concat([df[col].iloc[range(*idx[i])] for i,col in enumerate(df.columns)],axis=1) return df2 def sacul_2(df, idx): df2 = df.apply(lambda x: x.iloc[range(*idx[df.columns.get_loc(x.name)])]) return df2 # Benchmark Index STD nRow, nCol = 1000, 500 df = DataFrame(rand(nRow, nCol)) idx = df.index[randint(nRow, size=(nCol, 2))].values idx.sort(axis=1) print('STD') for func in [yellowhat, GeorgeLPerkins, user3483203, user3483203_2, user3483203_mod, piRSquared, sacul, sacul_2]: nmFunc = func.__name__ print(nmFunc) t = Timer("%s(df, idx)"%nmFunc, "from __main__ import df, idx, %s"%nmFunc).timeit(10) print(' %8.2f sec'%t) print('') # Benchmark Index GEN idx = linspace(0, 1, nRow) df = DataFrame(rand(nRow, nCol)).set_index(idx) idx = idx[randint(nRow, size=(nCol, 2))] idx.sort(axis=1) print('GEN') for func in [yellowhat, GeorgeLPerkins, user3483203_mod]: nmFunc = func.__name__ print(nmFunc) t = Timer("%s(df, idx)"%nmFunc, "from __main__ import df, idx, %s"%nmFunc).timeit(10) print(' %8.2f sec'%t) print('')
谢谢大家的回答。我不确定这是否真的比您拥有的更好,但您可以迭代您的列,使用
将*
索引解压缩到一个范围内,并连接生成的数据帧:
或者另一种方法,使用apply:使用每个列的索引号,使用pd.concat([df[col].iloc[range(*index[i])] for i,col in enumerate(df.columns)],axis=1) 0 1 2 1 NaN NaN 22.0 2 NaN 13.0 23.0 6 6.0 NaN NaN 7 7.0 NaN NaN 8 8.0 NaN NaN
为索引列表编制索引:df.columns.get_loc(x.name)
然后选择stack
loc
构造新系列,然后取消堆栈
您可以使用字典执行以下操作,而不是将“索引”作为列表:0 1 2 0.1 NaN NaN 21.0 0.2 NaN 12.0 22.0 0.6 5.0 NaN NaN 0.7 6.0 NaN NaN 0.8 7.0 NaN NaN 0.9 8.0 NaN NaN
更新:我询问了如何将这个问题的方法矢量化,@Divakar发布了一个可以在这里应用的方法:import pandas as pd lst = [ range(10), range(11,21), range(21,31) ] df = pd.DataFrame(lst).T dict = {0:[6,9], 1:[2,3], 2:[1,3]} df2 = pd.DataFrame(columns = df.columns, index=df.index) for k in dict: df2[k] = df[k][dict[k][0]:dict[k][1]+1] print(df2)
输出:r = np.arange(df.shape[0])[:, None] m = (idx[:,0] <= r) & (idx[:,1] > r) df.mask(~m).dropna(how='all') 0 1 2 1 NaN NaN 22.0 2 NaN 13.0 23.0 6 6.0 NaN NaN 7 7.0 NaN NaN 8 8.0 NaN NaN
0 1 2 1 NaN NaN 22.0 2 NaN 13.0 23.0 6 6.0 NaN NaN 7 7.0 NaN NaN 8 8.0 NaN NaN
使用
然后out=np.zeros(a,dtype=np.bool8)
@piRSquared我问了另一个问题,看看如何将其矢量化,Divakar当然有一个很好的解决方案。这很好。然而,广播可能会破坏时间复杂性和内存。有时循环更好。但现在我已经说过了,我需要支持它。。。这个解决方案似乎很有趣,但如果索引不是标准的,比如1,2,3,。。。我用更通用的索引更新我的问题。谢谢out[i:k,k]=True
r = np.arange(df.shape[0])[:, None] m = (idx[:,0] <= r) & (idx[:,1] > r) df.mask(~m).dropna(how='all') 0 1 2 1 NaN NaN 22.0 2 NaN 13.0 23.0 6 6.0 NaN NaN 7 7.0 NaN NaN 8 8.0 NaN NaN
def foo(a, idx): out = np.zeros(a, dtype=np.bool8) for (i, j), k in zip(idx, np.arange(a[1])): out[i:j, k] = True return out df.mask(~foo(df.shape, idx)).dropna(how='all')
0 1 2 1 NaN NaN 22.0 2 NaN 13.0 23.0 6 6.0 NaN NaN 7 7.0 NaN NaN 8 8.0 NaN NaN