矢量化 Pandas DataFrame

Vectorization Pandas DataFrame

假设以下简化框架:

我有一个 3D Pandas 参数数据框,由 100 行、4 classes 和每个实例的 4 个特征组成:

iterables = [list(range(100)), [0,1,2,3]]
index = pd.MultiIndex.from_product(iterables, names=['instances', 'classes'])
columns = ['a', 'b', 'c', 'd']
np.random.seed(42)
parameters = pd.DataFrame(np.random.randint(1, 2000, size=(len(index), len(columns))), index=index, columns=columns)

parameters

instances classes   a     b     c     d                     
0         0        1127  1460   861  1295
          1        1131  1096  1725  1045
          2        1639   122   467  1239
          3         331  1483    88  1397
1         0        1124   872  1688   131
...                 ...   ...   ...   ...
98        3        1321  1750   779  1431
99        0        1793   814  1637  1429
          1        1370  1646   420  1206
          2         983   825  1025  1855
          3        1974   567   371   936

df 为每个实例和每个特征(列)的数据框,报告观察到的 class.

np.random.seed(42)
df = pd.DataFrame(np.random.randint(0, 3, size=(100, len(columns))), index=list(range(100)), 
                  columns=columns)
    a  b  c  d
0   2  0  2  2
1   0  0  2  1
2   2  2  2  2
3   0  2  1  0
4   1  1  1  1
.. .. .. .. ..
95  1  2  0  1
96  2  1  2  1
97  0  0  1  2
98  0  0  0  1
99  1  2  2  2

我想创建形状为 (100, 4) 的第三个数据框(我们称之为 new_df),其中包含基于观察到的数据框 parameters 中的参数 class数据框上的 es df.

例如,在第一列 df 的第一行 (a) 我观察到 class 2,所以我感兴趣的值是第二个 class在 parameters 数据帧的第一个实例中,即 1127 将填充 new df 的第一行和第一列。按照这种方法,“b”列的第一个观察值是 class 0,所以在第一行,new_df 的 b 列我想观察 1460 等等。

通过for循环我可以获得想要的结果:

new_df = pd.DataFrame(0, index=list(range(100)), columns=columns) # initialize the df
for i in range(len(df)):
    for c in df.columns:
        new_df.iloc[i][c] = parameters.loc[i][c][df.iloc[i][c]]

new_df

    a     b      c    d
0   1639  1460   467  1239
1   1124   872   806   344
2   1083   511  1706  1500
3    958  1155  1268   563
4     14   242   777  1370
..   ...   ...   ...   ...
95  1435  1316  1709   755
96   346   712   363   815
97  1234   985   683  1348
98   127  1130  1009  1014
99  1370   825  1025  1855

然而,原始数据集包含数百万行和数百列,无法进行for循环。

有没有办法向量化这样的问题以避免for循环? (至少超过一维)

使用 stack 将两个 DataFrame 重塑为长格式,然后使用 unstack 执行合并和重塑,回到宽格式。有一堆重命名,所以我们可以在合并中引用和对齐列。

(df.rename_axis(index='instances', columns='cols').stack().to_frame('classes')
   .merge(parameters.rename_axis(columns='cols').stack().rename('vals'),
          on=['instances', 'classes', 'cols'])
   .unstack(-1)['vals']
   .rename_axis(index=None, columns=None)
)

       a     b     c     d
0   1639  1460   467  1239
1   1124   872   806   344
2   1083   511  1706  1500
3    958  1155  1268   563
4     14   242   777  1370
..   ...   ...   ...   ...
95  1435  1316  1709   755
96   346   712   363   815
97  1234   985   683  1348
98   127  1130  1009  1014
99  1370   825  1025  1855