在规范化Pandas数据时加速循环

use*_*074 6 python numpy vectorization pandas

我有一个pandas数据帧:

|  col1  | heading |
|--------|---------|
|heading1|   true  |
|abc     |  false  |
|efg     |  false  |
|hij     |  false  |
|heading2|   true  |
|klm     |  false  |
|...     |  false  |
Run Code Online (Sandbox Code Playgroud)

这个数据实际上是"顺序的",我想把它转换成这个结构:

|  col1  |  Parent   |
|---------------------
|heading1|  heading1 |
|abc     |  heading1 | 
|efg     |  heading1 |
|hij     |  heading1 |
|heading2|  heading2 |
|klm     |  heading2 |
|...     |  headingN |
Run Code Online (Sandbox Code Playgroud)

我有+ 10M行,所以这个方法需要太长时间:

df['Parent'] = df['col1']

for index, row in df.iterrows():
    if row['heading']:
        current = row['col1']
    else:
        row.loc[index, 'Parent'] = current
Run Code Online (Sandbox Code Playgroud)

您对更快的流程有什么建议吗?

use*_*203 5

您可以使用mask具有ffill:

df.assign(heading=df.col1.mask(~df.col1.str.startswith('heading')).ffill())
Run Code Online (Sandbox Code Playgroud)

       col1   heading
0  heading1  heading1
1       abc  heading1
2       efg  heading1
3       hij  heading1
4  heading2  heading2
5       klm  heading2
Run Code Online (Sandbox Code Playgroud)

这是通过更换不以启动任何价值headingNaN,然后填充的最后一个非楠价值前锋:

df.col1.mask(~df.col1.str.startswith('heading'))
Run Code Online (Sandbox Code Playgroud)

0    heading1
1         NaN
2         NaN
3         NaN
4    heading2
5         NaN
Name: col1, dtype: object
Run Code Online (Sandbox Code Playgroud)

df.col1.mask(~df.col1.str.startswith('heading')).ffill()
Run Code Online (Sandbox Code Playgroud)

0    heading1
1    heading1
2    heading1
3    heading1
4    heading2
5    heading2
Name: col1, dtype: object
Run Code Online (Sandbox Code Playgroud)


And*_*eak 3

可能不是一个非常符合熊猫习惯的解决方案,但您可以使用cumsum逻辑列并使用它来获取每行的相应标题。True本质上,我们定义了一个分段常数索引数组,该数组仅针对原始列上的每个值递增heading

import pandas as pd

# set up some dummy data
df = pd.DataFrame({'heading': [True, False, False, False, True, False, False]},
                  index=['heading1', 'foo', 'bar', 'baz', 'heading2', 'quux', 'quuz'])

# get every 'heading' index
headings = df.index[df.heading]
# fetch which row corresponds to which 'heading'
indices = df.heading.cumsum() - 1
# fetch the actual headings for each row
df['parent'] = headings[indices]

print(df)
Run Code Online (Sandbox Code Playgroud)

上述代码的输出是

          heading    parent
heading1     True  heading1
foo         False  heading1
bar         False  heading1
baz         False  heading1
heading2     True  heading2
quux        False  heading2
quuz        False  heading2
Run Code Online (Sandbox Code Playgroud)

您可以从中删除drop不必要的heading列。当然,您可以直接获取您拥有的逻辑数组并使用它:

headline = df.index.str.startswith('heading') # bool Series
headings = df.index[headline]
indices = df.heading.cumsum() - 1
df['parent'] = headings[indices]
Run Code Online (Sandbox Code Playgroud)