我有一个熊猫数据框:
| col1 | heading |
|--------|---------|
|heading1| true |
|abc | false |
|efg | false |
|hij | false |
|heading2| true |
|klm | false |
|... | false |
此数据实际上是“顺序的”,我想将其转换为以下结构:
| col1 | Parent |
|---------------------
|heading1| heading1 |
|abc | heading1 |
|efg | heading1 |
|hij | heading1 |
|heading2| heading2 |
|klm | heading2 |
|... | headingN |
我有+ 10M行,因此此方法花费的时间太长:
df['Parent'] = df['col1']
for index, row in df.iterrows():
if row['heading']:
current = row['col1']
else:
row.loc[index, 'Parent'] = current
您对更快的流程有什么建议吗?
答案 0 :(得分:5)
您可以将mask
与ffill
一起使用:
df.assign(heading=df.col1.mask(~df.col1.str.startswith('heading')).ffill())
col1 heading
0 heading1 heading1
1 abc heading1
2 efg heading1
3 hij heading1
4 heading2 heading2
5 klm heading2
这可以通过将所有不以heading
开头的值替换为NaN
的方式起作用,然后填充最后一个非nan值:
df.col1.mask(~df.col1.str.startswith('heading'))
0 heading1
1 NaN
2 NaN
3 NaN
4 heading2
5 NaN
Name: col1, dtype: object
df.col1.mask(~df.col1.str.startswith('heading')).ffill()
0 heading1
1 heading1
2 heading1
3 heading1
4 heading2
5 heading2
Name: col1, dtype: object
答案 1 :(得分: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)
以上代码的输出为
heading parent
heading1 True heading1
foo False heading1
bar False heading1
baz False heading1
heading2 True heading2
quux False heading2
quuz False heading2
您可以从中drop
不必要的heading
列中。当然,您可以直接获取您拥有的逻辑数组并对其进行处理:
headline = df.index.str.startswith('heading') # bool Series
headings = df.index[headline]
indices = df.heading.cumsum() - 1
df['parent'] = headings[indices]
答案 2 :(得分:2)
我也想到了填补。通过使用df.pop(),我们可以确保该列也消失了。
df['Parent'] = df['col1'].mul(df.pop('heading')).replace('',np.nan).ffill()
完整示例
import pandas as pd
import numpy as np
df = pd.DataFrame({
'col1': ['heading1', 'abc', 'efg', 'hij', 'heading2', 'klm'],
'heading': [True, False, False, False, True, False]
})
df['Parent'] = df['col1'].mul(df.pop('heading')).replace('',np.nan).ffill()
print(df)
返回:
col1 Parent
0 heading1 heading1
1 abc heading1
2 efg heading1
3 hij heading1
4 heading2 heading2
5 klm heading2
答案 3 :(得分:1)
where
+ pop
+ ffill
您可能会发现这更有效。来自@AntonvBR的数据。
df['Parent'] = df['col1'].where(df.pop('heading')).ffill()
print(df)
col1 Parent
0 heading1 heading1
1 abc heading1
2 efg heading1
3 hij heading1
4 heading2 heading2
5 klm heading2
答案 4 :(得分:1)
要使用完全不同的方法,可以通过遍历布尔数组并随后将其用作标头的映射来获取索引。我不知道标题映射的速度有多快,但是您可以快速为布尔值建立索引。
import numpy as np
from numba import jit
bool_array = np.array([True, False], dtype=np.bool)
boolean_array = np.random.choice(bool_array, size=100000000)
@jit(nopython=True)
def reassign(boolean_array):
b = np.zeros(shape=(len(boolean_array),), dtype=np.int32)
b[0] = 0
for i in range(1,len(boolean_array)):
if boolean_array[i]:
b[i] = i
else:
b[i] = b[i-1]
return b
import time
start = time.time()
print(reassign(boolean_array))
print("took {} seconds".format(time.time()-start))
使用Numba拍摄0.5秒,不使用Numba拍摄130秒,持续100毫米