Python中pd.wide_to_long速度慢

1 投票
1 回答
54 浏览
提问于 2025-04-14 16:27

我有一个数据表,里面有55049行和667列。

下面是这个数据表的结构示例:


data = {
    'g1': [1],
    'g2': [2],
    'g3': [3],
    
    'st1_1': [1],
    'st1_2': [1],
    'st1_3': [1],
    'st1_4': [1],
    'st1_5': [5],
    'st1_6': [5],
    'st1_7': [5],
    'st1_8': [5],
    
    'st1_Next_1': [8],
    'st1_Next_2': [8],
    'st1_Next_3': [8],
    'st1_Next_4': [8],
    'st1_Next_5': [9],
    'st1_Next_6': [9],
    'st1_Next_7': [9],
    'st1_Next_8': [9],
    
    'st2_1': [2],
    'st2_2': [2],
    'st2_3': [2],
    'st2_4': [2],
    'st2_5': [2],
    'st2_6': [2],
    'st2_7': [2],
    'st2_8': [2],
    
    'ft_1': [1],
    'ft_2': [0],
    'ft_3': [1],
    'ft_4': [1],
    'ft_5': [1],
    'ft_6': [0],
    'ft_7': [0],
    'ft_8': [1]
}

df = pd.DataFrame(data)
print(df)

为了得到我想要的结果,我使用了 pd.wide_to_long 这个代码。

ilist = ['g1','g2','g3']
stublist = ['st1','st1_Next','st2','ft']

df_long = pd.wide_to_long(
      df.reset_index(),
      i=['index']+ilist ,
      stubnames= stublist,
      j='j', sep='_').reset_index()

df_long = df_long[df_long['ft']==1]

上面的代码运行得很好,得到了预期的结果。

我使用这个 wide_to_long 方法来应用过滤条件 df_long[df_long['ft']==1]。这意味着 ft_1 需要应用于所有 _1,ft_2 需要应用于所有 _2……以此类推,直到 _8。

问题是,执行 wide_to_long 操作大约花了2分钟。因为我有800多个源文件要处理,整个过程需要1600分钟,这实在是太长了。

我在寻找其他方法来转置这些数据。

我尝试过 这个方法,但对我来说效果不大。

根据 @sammywemmy 的建议,我尝试了下面的代码。但输出中缺少 st1_Next


ilist = ['g1','g2','g3']
stublist = ['st1','st1_Next','st2','ft']


df_pvot = df.pivot_longer(index=ilist,names_to=stublist,names_pattern=stublist)
print(df_pvot)

输出中缺少 st1_Next,数据和 st1 合并在一起,而不是形成新的列。

Output:

    g1  g2  g3  st1  st2   ft
0    1   2   3    1  2.0  1.0
1    1   2   3    1  2.0  0.0
2    1   2   3    1  2.0  1.0
3    1   2   3    1  2.0  1.0
4    1   2   3    5  2.0  1.0
5    1   2   3    5  2.0  0.0
6    1   2   3    5  2.0  0.0
7    1   2   3    5  2.0  1.0
8    1   2   3    8  NaN  NaN
9    1   2   3    8  NaN  NaN
10   1   2   3    8  NaN  NaN
11   1   2   3    8  NaN  NaN
12   1   2   3    9  NaN  NaN
13   1   2   3    9  NaN  NaN
14   1   2   3    9  NaN  NaN
15   1   2   3    9  NaN  NaN

1 个回答

2

一个方法是使用 pivot_longer,在这里你需要把新的表头名称传给 names_to,然后把一系列正则表达式传给 names_pattern

# pip install pyjanitor
import pandas as pd

df.pivot_longer(index=ilist,names_to=stublist,names_pattern=stublist)
   g1  g2  g3  st1  st2  ft
0   1   2   3    1    2   1
1   1   2   3    1    2   0
2   1   2   3    1    2   1
3   1   2   3    1    2   1
4   1   2   3    1    2   1
5   1   2   3    1    2   0
6   1   2   3    1    2   0
7   1   2   3    1    2   1

另一个方法是先调整列的形状,然后使用 pd.stack:

reshaped = df.set_index(ilist)
reshaped.columns = reshaped.columns.str.split('_',expand=True).set_names([None,'drop'])
reshaped.stack(level='drop').droplevel('drop').reset_index()
   g1  g2  g3  st1  st2  ft
0   1   2   3    1    2   1
1   1   2   3    1    2   0
2   1   2   3    1    2   1
3   1   2   3    1    2   1
4   1   2   3    1    2   1
5   1   2   3    1    2   0
6   1   2   3    1    2   0
7   1   2   3    1    2   1

这是针对更新后问题的回答 - names_pattern 依赖于正则表达式,底层使用了 pd.Series.str.containsnp.select 来提取和配对列与正则表达式。因此,正则表达式需要正确编写,以匹配这些列:

# pip install pyjanitor
import pandas as pd
import janitor 

# note the inclusion of digits within the regexes
names_pattern = [r'st1_\d+',r'st1_Next',r'st2_\d+', 'ft']

df.pivot_longer(index=ilist,names_to=stublist,names_pattern=names_pattern)
   g1  g2  g3  st1  st1_Next  st2  ft
0   1   2   3    1         8    2   1
1   1   2   3    1         8    2   0
2   1   2   3    1         8    2   1
3   1   2   3    1         8    2   1
4   1   2   3    5         9    2   1
5   1   2   3    5         9    2   0
6   1   2   3    5         9    2   0
7   1   2   3    5         9    2   1

你可以使用 stack 来实现这一点,这次使用 pd.Series.str.rsplit(),并设置 n=1

reshaped = df.set_index(ilist)
reshaped.columns = (reshaped
                    .columns
                    .str
                    .rsplit('_',n=1,expand=True)
                    .set_names([None, 'drop'])
                   )

reshaped.stack(level='drop').droplevel('drop').reset_index()
   g1  g2  g3  st1  st1_Next  st2  ft
0   1   2   3    1         8    2   1
1   1   2   3    1         8    2   0
2   1   2   3    1         8    2   1
3   1   2   3    1         8    2   1
4   1   2   3    5         9    2   1
5   1   2   3    5         9    2   0
6   1   2   3    5         9    2   0
7   1   2   3    5         9    2   1

只要你理解你的列的模式,并根据这些模式调整你的代码,就没问题。

撰写回答