Pandas 将数据帧的多个指定位置分配给序列

Pandas 将数据帧的多个指定位置分配给序列,pandas,dataframe,series,Pandas,Dataframe,Series,假设我有一个掩码数据帧(称为mask),它告诉我数据(称为c1或c2)应该放在哪里: mask = pd.DataFrame([ [0,0,1], [0,0,1], [1,0,0], [1,0,0], [1,1,0], [0,1,0],

假设我有一个掩码数据帧(称为
mask
),它告诉我数据(称为
c1
c2
)应该放在哪里:

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),数据应该放在这些补丁中;将每一个都称为有效补丁。我有两个感兴趣的案例:

  • c1
    是应该进入
    mask
    中所有列的所有有效补丁的数据;即,所需输出为:
  • c2
    中的列应进入
    mask
    对应列补丁中的所有有效补丁;即,所需输出为:
  • *一个细节:每个垂直面片几乎总是确定为给定长度N,其中N为奇数(此处N=3),但边界可能存在问题;在这种情况下,我希望每个有效补丁的中点与要插入数据的中点对齐(要么是
    c1
    ,要么是
    c2
    的相应列)。确保每个有效补片的长度至少为(N+1)/2,即中点加上两侧其中一侧的至少一半


    如何在不循环列并依次查找每列中所有有效修补程序的位置的情况下执行此操作?

    此方法循环列,但使用所有矢量化操作,因此速度应该很快。如果
    c2
    的长度是奇数,并且
    遮罩中的所有条纹也是奇数长度,并且>=到
    c2
    的长度,则此方法有效。对于那些不符合这些条件的群体,我们需要做出调整

    首先重新定义c2的索引。这是一个0附近的对称计数器。这将允许我们将c2中的每个系列映射到条纹。对于遮罩,将连续的1转换为数字,其中0表示中间(因为您的条纹总是奇数),然后我们在上面和下面计数。这意味着5 1s的条纹将得到[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)
    


    如果您需要使用
    c1
    映射所有内容,则只需进行很少的更改。将其转换为一个系列,并将其用于映射,而不是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)
    

    如果截断在底部,它需要
    np.floor
    ,否则这就行了。@MaviPranav啊,是的,我不确定这些是否需要以不同的方式处理。如果只是边缘问题,您可能可以安全地在下半部分应用地板,在上半部分应用天花板。
    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
    
    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)