Python Dataframe:为每列选择不同的索引

Python 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

假设我有以下数据帧:

从导入数据帧 来自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.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
也许不是更好的解决办法

谢谢大家的回答

比较 我写了一个脚本来测试每个答案。 基准分为两部分:

  • STD
    :仅适用于标准索引(0,1,2,3,…)的答案
  • 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('')
    

    谢谢大家的回答。

    我不确定这是否真的比您拥有的更好,但您可以迭代您的列,使用
    *
    索引解压缩到一个范围内,并连接生成的数据帧:

    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
    
    或者另一种方法,使用apply:使用每个列的索引号,使用
    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
    
    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)
    
    更新:我询问了如何将这个问题的方法矢量化,@Divakar发布了一个可以在这里应用的方法:

    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)
    然后
    out[i:k,k]=True
    @piRSquared我问了另一个问题,看看如何将其矢量化,Divakar当然有一个很好的解决方案。这很好。然而,广播可能会破坏时间复杂性和内存。有时循环更好。但现在我已经说过了,我需要支持它。。。这个解决方案似乎很有趣,但如果索引不是标准的,比如1,2,3,。。。我用更通用的索引更新我的问题。谢谢
    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