【问题标题】:Best way to flatten dataframe based on values on column根据列上的值展平数据框的最佳方法
【发布时间】:2019-03-21 05:38:24
【问题描述】:

我必须处理包含数十万行的整个数据框,但我可以将其简化如下:

df = pd.DataFrame([
('a', 1, 1),
('a', 0, 0),
('a', 0, 1),
('b', 0, 0),
('b', 1, 0),
('b', 0, 1),
('c', 1, 1),
('c', 1, 0),
('c', 1, 0)
], columns=['A', 'B', 'C'])

print (df)

   A  B  C
0  a  1  1
1  a  0  0
2  a  0  1
3  b  0  0
4  b  1  0
5  b  0  1
6  c  1  1
7  c  1  0
8  c  1  0

我的目标是根据“A”列中的标签来展平“B”和“C”列

   A  B_1  B_2  B_3  C_1  C_2  C_3
0  a    1    0    0    1    0    1
3  b    0    1    0    0    0    1
6  c    1    1    1    1    0    0

我编写的代码给出了我想要的结果,但是它非常慢,因为它在唯一标签上使用了一个简单的 for 循环。 我看到的解决方案是编写一些优化我的代码的向量化函数。有人有什么想法吗? 下面我附上代码。

added_col = ['B_1', 'B_2', 'B_3', 'C_1', 'C_2', 'C_3']

new_df = df.drop(['B', 'C'], axis=1).copy()
new_df = new_df.iloc[[x for x in range(0, len(df), 3)], :]
new_df = pd.concat([new_df,pd.DataFrame(columns=added_col)], sort=False)

for e, elem in new_df['A'].iteritems():
    new_df.loc[e, added_col] = df[df['A'] == elem].loc[:,['B','C']].T.values.flatten()

【问题讨论】:

  • 为一个结构合理的问题点赞。 :)
  • 我的直觉认为,在你进行的过程中构建一个新数组,而不是每次迭代都调用 flatten(),这样会大大加快速度。

标签: pandas dataframe vectorization


【解决方案1】:

为了提高性能,我使用了 numba 和 numpy 赋值

from numba import njit

@njit
def f(i, vals, n, m, k):

  out = np.empty((n, k, m), vals.dtype)
  out.fill(0)

  c = np.zeros(n, np.int64)

  for j in range(len(i)):
    x = i[j]
    out[x, :, c[x]] = vals[j]
    c[x] += 1

  return out.reshape(n, m * k)


d0 = df.drop('A', 1)
cols = [*d0]

i, r = pd.factorize(df.A)

n = len(r)
m = np.bincount(i).max()
k = len(cols)

vals = d0.values

pd.DataFrame(
    f(i, vals, n, m, k),
    pd.Index(r, name='A'),
    [f"{c}_{i}" for c in cols for i in range(1, m + 1)]
).reset_index()

   A  B_1  B_2  B_3  C_1  C_2  C_3
0  a    1    0    0    1    0    1
1  b    0    1    0    0    0    1
2  c    1    1    1    1    0    0

【讨论】:

    【解决方案2】:

    使用%修改您的索引

    df.index=df.index%3+1
    df.set_index('A',append=True,inplace=True)
    newdf=df.unstack(level=0)
    newdf.columns=newdf.columns.map('{0[0]}_{0[1]}'.format)
    newdf
    Out[291]: 
       B_1  B_2  B_3  C_1  C_2  C_3
    A                              
    a    1    0    0    1    0    1
    b    0    1    0    0    0    1
    c    1    1    1    1    0    0
    

    【讨论】:

      【解决方案3】:

      使用groupbyravel() 的另一种方法

      >>> df.groupby('A')[['B','C']].apply(lambda s: pd.Series(s.T.values.ravel(), 
                                                               index=[f'{x}_{i}' for x in s.columns for i in range(1, len(s)+1)]))
      
          B_1 B_2 B_3 C_1 C_2 C_3
      A                       
      a   1   0   0   1   0   1
      b   0   1   0   0   0   1
      c   1   1   1   1   0   0
      

      【讨论】:

        【解决方案4】:

        这是一种方法:

        # create a row number by group
        df['rn'] = df.groupby('A').cumcount() + 1
        
        # pivot the table
        new_df = df.set_index(['A', 'rn']).unstack()
        
        # rename columns
        new_df.columns = [x + '_' + str(y) for (x, y) in new_df.columns]
        
        new_df.reset_index()
        #   A  B_1  B_2  B_3  C_1  C_2  C_3
        #0  a    1    0    0    1    0    1
        #1  b    0    1    0    0    0    1
        #2  c    1    1    1    1    0    0
        

        【讨论】:

          猜你喜欢
          • 2019-06-21
          • 2016-01-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-04
          • 1970-01-01
          相关资源
          最近更新 更多