选择MultiIndexed数据框中的行

时间:2017-01-24 16:18:56

标签: python pandas numpy

我想单独提取'S'的箱子,其中每列(X& Y)> 0.5或多个箱> 0.5 *'行数'。

在示例中;

对于'AR1'应该只选择bin 4,因为'X'和'Y'是> 0.5(蓝色表示)

对于'PO1'应该选择区分1,2,3和4,因为'X'和'Y'是> (4 * 0.5)(黄色表示)。

我之前用for loop尝试了这个,但是没有正常工作; Selecting multiple (neighboring) rows conditionally

np.random.seed(0)

N = 20
S = ['AR1', 'PO1']

df = pd.DataFrame(
    {'X':np.random.uniform(-1,1,N),
     'Y':np.random.uniform(-1,1,N),
     'S':np.random.choice(S,N),
    })

df['bins_X'] = df.groupby('S')['X'].apply(pd.qcut, q=5, labels=np.arange(5))    # create bins per column 'S'

def func(df):                                                                   # create function to group per 'S' and their bins
    df1 = df.groupby(['S','bins_X']).sum()
    new_cols= list(zip(df1.columns.get_level_values(0)))
    df1.columns = pd.MultiIndex.from_tuples(new_cols)
    return df1

print func(df)

enter image description here

修改

它的外观应该是问题所示的df,但不符合条件的行会被过滤掉。我检查的是这个; X和Y中的值>任何行(bin)单独或组合0.5。行的组合仅连续,2,3,4或5行组合。

即,0的行组合是; 0 + 1,0 + 1 + 2,0 + 1 + 2 + 3和0 + 1 + 2 + 3 + 4。为1; 1 + 2,1 + 2 + 3和1 + 2 + 3 + 4等。

多行将总和为行数x 0.5,X和Y必须为> 2.5例如0到4行。

EDIT2: @JohnE和piRSquared,你的解决方案都有效,但是当数据框中有其他列不应该被评估时,哪一个会更好?

此外,如果我想在您的解决方案中添加其他条件,该怎么办?

EDIT3: @piRSquared,当对某些列进行子集化时,我只返回那些列,我需要所有列,而不仅仅是子集。

你能帮忙吗?感谢。

1 个答案:

答案 0 :(得分:3)

这是一种矢量化方法,顶级只有一个循环(groupby.apply

# columns that I care about
cols = ['X', 'Y']
df1.groupby(level=0)[cols].apply(find_window)

enter image description here

def find_window(df):
    v = df.values
    s = np.vstack([np.zeros((1, v.shape[1])), v.cumsum(0)])

    threshold = .5

    r, c = np.triu_indices(s.shape[0], 1)
    d = (c - r)[:, None]
    e = s[c] - s[r]
    mask = (e / d > threshold).all(1)
    rng = np.arange(mask.shape[0])

    if mask.any():
        idx = rng[mask][d[mask].argmax()]

        i0, i1 = r[idx], c[idx]
        return pd.DataFrame(
            v[i0:i1],
            df.loc[df.name].index[i0:i1],
            df.columns
        )

解释

策略

  • numpy.triu_indices:我需要评估滚动mean的每个可能窗口大于某些threshold。我将捕获每个可能的窗口,从位置0开始到0,然后从0开始到1然后......然后是1到1,1到2 ......等等。但在结束之前,我必须始终站在一个位置。我可以使用numpy.triu_indices
  • 访问这些组合
  • cumsum:获取由np.triu_indices得到的每个索引组合指定的扩展数组会有点棘手(可行)。更好的方法是计算cumsum并将一个索引与下一个索引区分开来。
  • 我必须在我的cumsum前面添加零,以便我可以为第一行添加差异。
  • 但总和不是手段。我需要除以行数来获得平均值。方便的是,结束位置和起始位置之间的差异恰好是行数,因此是除以总和以计算均值的适当数量。
  • 现在我有了e / d,我检查哪些是> threshold,并确定哪些开始和结束位置的组合意味着大于两个列的阈值。
  • 然后,我确定那些有大于阈值的行中行数最多的组合。
  • 我展开位置并重建数据框
  • groupbyapply ... QED

时间测试

enter image description here

包含更多数据

np.random.seed(0)

N = 300
S = ['AR1', 'PO1', 'AR2', 'PO2', 'AR3', 'PO3']

df = pd.DataFrame(
    {'X':np.random.uniform(-1,1,N),
     'Y':np.random.uniform(-1,1,N),
     'S':np.random.choice(S,N),
    })

df['bins_X'] = df.groupby('S')['X'].apply(pd.qcut, q=20, labels=np.arange(20))    # create bins per column 'S'

def func(df):                                                                   # create function to group per 'S' and their bins
    df1 = df.groupby(['S','bins_X']).sum()
    new_cols= list(zip(df1.columns.get_level_values(0)))
    df1.columns = pd.MultiIndex.from_tuples(new_cols)
    return df1

df1 = func(df)

时差更显着

enter image description here