"Pivot" Pandas 数据帧到 3D 数字数组中



给定具有以下结构的数据帧:

Date     | Site  | Measurement Type | Value
-----------------------------------------------
1/1/2020 | A     | Temperature      | 32.3
1/2/2020 | B     | Humidity         | 70%

我想创建一个 3D "数据透视表",其中第一个轴表示站点,第二个轴表示日期,第三个轴表示测量类型,值存储在每个元素中。

例如,如果我在 5 个站点进行一周的每日测量,同时测量温度和湿度,则所需的输出将是一个形状为 (5, 7, 2( 的数组。

Pandas 似乎只支持创建 2D 数据透视表,但我只对一个未标记的 3D numpy 数组作为输出感到满意。想知道在我花时间自己实现它之前是否有一种现有的简单方法可以做到这一点。

使用df.pivot_table是可行的。我在您的示例中又添加了一行以使两个Measurement Type.在缺失值上,它将用np.nan表示

sample `df`
Date Site Measurement_Type Value
0  1/1/2020    A      Temperature  32.3
1  1/1/2020    A         Humidity   60%
2  1/2/2020    B         Humidity   70%

尝试以下操作

iix = pd.MultiIndex.from_product([np.unique(df.Date), np.unique(df.Measurement_Type)])
df_pivot = (df.pivot_table('Value', 'Site', ['Date', 'Measurement_Type'], aggfunc='first')
.reindex(iix, axis=1))
arr = np.array(df_pivot.groupby(level=0, axis=1).agg(lambda x: [*x.values])
.to_numpy().tolist())
print(arr)
Out[1447]:
array([[['60%', '32.3'],
[nan, nan]],
[[nan, nan],
['70%', nan]]], dtype=object)

方法2:在不同的列和numpyreshape上使用pivot_table

iix_n = pd.MultiIndex.from_product([np.unique(df.Site), np.unique(df.Date)])
arr = (df.pivot_table('Value', ['Site', 'Date'], 'Measurement_Type', aggfunc='first')
.reindex(iix_n).to_numpy()
.reshape(df.Site.nunique(),df.Date.nunique(),-1))
Out[1501]:
array([[['60%', '32.3'],
[nan, nan]],
[[nan, nan],
['70%', nan]]], dtype=object)

我认为您正在寻找的是类似于panel的东西。您也可以只使用 3 暗淡的 numpy 数组。例如使用面板:

p_dim = {}
# desired columns
cols = ['Site', 'Measurement Type']
for date in df.Date:
sub_df = df[df.Date.isin([date])].reset_index(drop=True)
p_dim[date] = sub_df[[c for c in sub_df.columns if c in cols]]
panel = pd.Panel(p_dim)

现在,您可以使用panel['1/1/2020']访问与日期关联的各种数据,假设您的Date列是类型str。要查看所有可用密钥,您可以使用panel.keys()

我做了一个小脚本来测量不同@Andy L.方法的性能。

第二种方法似乎稍微快一些:

def pivot_table_3d_1(df, col1, col2, col3, value, aggfunc='first') :
iix = pd.MultiIndex.from_product([np.unique(df[col2]), np.unique(df[col3])])
df_pivot = (df.pivot_table(value, col1, [col2,col3], aggfunc=aggfunc)
.reindex(iix, axis=1))
arr = np.array(df_pivot.groupby(level=0, axis=1).agg(lambda x: [*x.values])
.to_numpy().tolist())
return arr
def pivot_table_3d_2(df, col1, col2, col3, value, aggfunc='first') :
iix_n = pd.MultiIndex.from_product([np.unique(df[col1]), np.unique(df[col2])])
arr = (df.pivot_table(value, [col1, col2], col3, aggfunc=aggfunc)
.reindex(iix_n).to_numpy()
.reshape(df[col1].nunique(),df[col2].nunique(),-1))
return arr
## TESTING
N1 = 100
N2 = 200
N3 = 300
df = pd.DataFrame({'col1': np.random.randint(0, N1, N1*N2*N3),
'col2': np.random.randint(0, N2, N1*N2*N3),
'col3': np.random.randint(0, N3, N1*N2*N3),
'value': np.random.normal(0,1,N1*N2*N3)})
%timeit pivot_table_3d(df, col1='col1', col2='col2', col3='col3', value='value')
# 10.2 s ± 39.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit pivot_table_3d_2(df, col1='col1', col2='col2', col3='col3', value='value')
#9.47 s ± 108 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

最新更新