将数据框的多个指定位置分配给一个系列

Assign multiple specified positions of a dataframe to a series

假设我有一个掩码数据框(称为 mask),它告诉我数据(称为 c1c2)应该去哪里:

mask = pd.DataFrame([
                    [0,0,1],
                    [0,0,1],
                    [1,0,0],
                    [1,0,0],
                    [1,1,0],
                    [0,1,0],
                    [1,1,0],
                    [1,0,1],
                    [1,0,1],
                    [0,0,1]],
                    columns = ['C1', 'C2', 'C3'],
                   index = np.arange(0,10))

c1 = ['a','b','c']
c2 = pd.DataFrame([
                  ['a1','a2','a3'],
                  ['b1','b2','b3'],
                  ['c1', 'c2','c3']], columns=['C1','C2','C3'])

mask 中的每一列都可以有几个 1 的补丁(具有给定的固定奇数长度*,这里是 3),数据应该放在这些地方;将其中的每一个都称为有效补丁。我有两个感兴趣的案例:

  1. c1 是应该进入 mask 中所有列的所有有效补丁的数据;也就是说,所需的输出是:
      pd.DataFrame([[0,    0, 'b'],
                    [0,    0, 'c'],
                    ['a',  0,  0],
                    ['b',  0,  0],
                    ['c', 'a', 0],
                    [0,   'b', 0],
                    ['a', 'c', 0],
                    ['b',  0, 'a'],
                    ['c',  0, 'b'],
                    [0,    0, 'c']],
                    columns = ['C1', 'C2', 'C3'],
                   index = np.arange(0,10))
  1. c2 中的列应该进入 mask 相应列的补丁中的所有有效补丁;也就是说,所需的输出是:
     pd.DataFrame([
                    [0,     0, 'b3'],
                    [0,     0, 'c3'],
                    ['a1',  0,   0],
                    ['b1',  0,   0],
                    ['c1', 'a2', 0],
                    [0,    'b2', 0],
                    ['a1', 'c2', 0],
                    ['b1',  0,  'a3'],
                    ['c1',  0,  'b3'],
                    [0,     0,  'c3']],
                    columns = ['C1', 'C2', 'C3'],
                   index = np.arange(0,10))

*一个细节:每个垂直面片几乎总是保证给定长度N,N为奇数(这里N=3),但在边界处可能存在问题;在这种情况下,我希望每个有效补丁的中点与要插入的数据的中点对齐(c1c2 的相应列)。确保每个有效补丁的长度至少为 (N+1)/2,即中点加上两侧之一的至少二分之一。

如何在不遍历列并按顺序查找每列中所有有效补丁的位置的情况下执行此操作?

此方法遍历列,但使用所有矢量化操作,因此应该很快。如果 c2 的长度是奇数并且 mask 中的所有条纹也是奇数长度并且 >= 到 c2 的长度,则此方法有效。对于不满足这些条件的群体,我们需要进行调整。

首先重新定义c2的索引。所以它是一个围绕 0 的对称计数器。这将使我们能够将 c2 中的每个系列映射到条纹。对于掩码,将连续的 1 转换为数字,其中 0 表示中间(因为你的条纹总是奇数),然后我们计算上面和下面。这意味着连续 5 个 1 将得到 [NaN, val1, val2, val3, NaN] 因此只有最中间的值被填充。

np.ceil 行有点乱,但如果您的组由于边缘而太小,则逻辑似乎是正确的。

import numpy as np

l = (len(c2)-1)/2
c2.index = np.arange(-l, l+1, 1)
#      C1  C2  C3
#-1.0  a1  a2  a3
# 0.0  b1  b2  b3
# 1.0  c1  c2  c3

df = mask.eq(0).cumsum().where(mask.ne(0))
for col in df.columns:
    df[col] = ((df.groupby(col).cumcount() - (df.groupby(col)[col].transform('size')-1)/2)
                 .where(df[col].notnull()))
    
    # Deal with edges or groups not odd length
    df[col] = np.ceil(df[col])
    
    # Turn counter within group to the value in c2
    df[col] = df[col].map(c2[col]).fillna(0)

print(df)

   C1  C2  C3
0   0   0  b3
1   0   0  c3
2  a1   0   0
3  b1   0   0
4  c1  a2   0
5   0  b2   0
6  a1  c2   0
7  b1   0  a3
8  c1   0  b3
9   0   0  c3

如果您需要用 c1 映射所有内容,那么只需很少改动。将其转换为系列并将其用于 map,而不是来自 c2.

的系列
l = (len(c1)-1)/2
s = pd.Series(c1, index=np.arange(-l, l+1, 1))
#-1.0    a
# 0.0    b
# 1.0    c

# All of the same code, just change this very last line within the loop to:
    df[col] = df[col].map(s).fillna(0)