数据帧如何基于列的子字符串透视表



我有一个数据帧:

df = 
time id ser1 ser2 ... ser20 N0ch0 N1ch0 N2ch0 N0ch1 N1ch1 N2ch1 N0ch2 N1ch2 N2ch2 N0ch3 N1ch3 N2ch3
1   2  4    5         3     8     7     8    5     1      4    6     2      7    9    8      6

我想根据通道('ch'子字符串(来调整它,这样它就会变成一列,所以新的数据帧将是:

time id channel ser1 ser2 ... ser20 N0 N1 N2
1   2   0      4    5         3   8  7  8
1   2   1      4    5         3   5  1  4
1   2   2      4    5         3   6  2  7
1   2   3      4    5         3   9  8  6

最好的方法是什么?

您可以从使用melt开始,参数id_vars设置为类似"ser"的列和"time"+"id"。

然后,您可以将"variable"列拆分为2,其中一列在使用pivot_table时将用作索引列,另一列将用作column:

# Columns to be used as index in melt & pivot
id_cols = ['time','id'] + list(df.filter(like='ser'))
# Melt and split a column
m = df.melt(id_vars = id_cols)
m[['N','channel']] = m.variable.str.split('ch', 1 ,expand=True)
# Pivot the melted dataframe
out = m.pivot_table(index = id_cols + ['channel'],  columns='N', values='value').reset_index()

打印:

time  id channel  ser1  ser2  ser20  N0  N1  N2
0     1   2       0     4     5      3   8   7   8
1     1   2       1     4     5      3   5   1   4
2     1   2       2     4     5      3   6   2   7
3     1   2       3     4     5      3   9   8   6

我们可以使用set_index来保存任何应该修改的列。然后str.split"ch"上的其余列,这似乎是新列名和通道号之间的分隔符。然后是stackreset_index,以便从MultiIndex列转换为长格式。接下来是astype,将新通道列从字符串转换为int(如果需要(。

# columns to save
idx_cols = ['time', 'id', 'ser1', 'ser2']
res = df.set_index(idx_cols)
# Separate N value from channel number
res.columns = res.columns.str.split('ch', expand=True).rename([None, 'channel'])
# Go to long form
res = res.stack().reset_index()
# Convert to number from string
res['channel'] = res['channel'].astype(int)

res:

time  id  ser1  ser2  channel  N0  N1  N2
0     1   2     4     5        0   8   7   8
1     1   2     4     5        1   5   1   4

或者,可以使用wide_to_long,它抽象了一些整形,但需要后续的str.extract来获得信道号,并手动指定所有";stubnames":

# columns to save
idx_cols = ['time', 'id', 'ser1', 'ser2']
res = (
pd.wide_to_long(
df,
i=idx_cols,
j='channel',
stubnames=['N0', 'N1', 'N2'],  # all stub names (add more if needed)
suffix=r'chd+'  # suffix
).reset_index()
)
# Get only the channel numbers and convert to int
res['channel'] = res['channel'].str.extract(r'(d+$)').astype(int)

res

time  id  ser1  ser2  channel  N0  N1  N2
0     1   2     4     5        0   8   7   8
1     1   2     4     5        1   5   1   4

任一选项的注意事项idx_cols可以动态创建,而不是手动创建。

通过对第一个n列进行切片(此示例代码为4列(:

idx_cols = df.columns[:4]

或者根据条件过滤DataFrame列(如str.startswith:

idx_cols = ['time', 'id', *df.columns[df.columns.str.startswith('ser')]]

样本设置:

import pandas as pd
df = pd.DataFrame({
'time': [1], 'id': [2], 'ser1': [4], 'ser2': [5],
'N0ch0': [8], 'N1ch0': [7], 'N2ch0': [8],
'N0ch1': [5], 'N1ch1': [1], 'N2ch1': [4]
})

最新更新