我的数据框df
包含['metric_type', 'metric_value']
列。对于每一行,我想确保我有一个名称等于'metric_type'
的列,该列的值等于'metric_value'
。
我的一个问题是'metric_type'
有虚假的空间,我想摆脱它。
考虑数据框df
:
df = pd.DataFrame([
['a ', 1],
[' b', 2],
[' c ', 3]
], columns=['metric_type', 'metric_value'])
print(df)
metric_type metric_value
0 a 1
1 b 2
2 c 3
请注意,'metric_type'
的每个值都有不同位置的空格。
我创建了一个使用apply
的函数,但这需要花费很长时间。
def assign_metric_vals(row):
row[row['metric_type'].replace(" ", "")] = row['metric_value']
return row
当我使用它时,我明白了:
a b c metric_type metric_value
0 1.0000 nan nan a 1
1 nan 2.00 nan b 2
2 nan nan 3.00 c 3
是否有更好的(阅读,#34;更快")方式来完成同样的任务?
答案 0 :(得分:11)
使用metric_type
和取消堆栈设置索引会更好。
df.set_index(df.metric_type.str.replace(' ', ''), append=True).metric_value.unstack()
演示
df = pd.DataFrame([
['a ', 1],
[' b', 2],
[' c ', 3]
], columns=['metric_type', 'metric_value'])
print(df)
metric_type metric_value
0 a 1
1 b 2
2 c 3
print(df.apply(assign_metric_vals, 1))
a b c metric_type metric_value
0 1.0000 nan nan a 1
1 nan 2.00 nan b 2
2 nan nan 3.00 c 3
或我的方式
idx = df.metric_type.str.replace(' ', '')
d1 = df.set_index(idx, append=True).metric_value.unstack()
print(pd.concat([d1, df], axis=1))
a b c metric_type metric_value
0 1.0000 nan nan a 1
1 nan 2.00 nan b 2
2 nan nan 3.00 c 3
时间
使用更大的df
df1 = pd.concat([df] * 30000, ignore_index=True)
%%timeit
idx = df1.metric_type.str.replace(' ', '')
d1 = df1.set_index(idx, append=True).metric_value.unstack()
pd.concat([d1, df1], axis=1)
10个循环,最佳3:每循环77.3毫秒
%%timeit
df1.apply(assign_metric_vals, 1)
1个循环,最佳3:每循环57.4秒
答案 1 :(得分:2)
这是一种替代方案,速度提高约20%,并提供与@ piRSquared相同的答案。我不会建议它更好或更差(一般情况下),但赏金是在接受答案后发布的,所以我会将此作为附加选项提供。
%%timeit
idx = df1.metric_type.str.replace(' ', '')
d1 = df1.set_index(idx, append=True).metric_value.unstack()
result1 = pd.concat([d1, df1], axis=1)
10 loops, best of 3: 97.6 ms per loop
%%timeit
df1.metric_type = df1.metric_type.str.strip()
d1 = df1.pivot(columns='metric_type', values='metric_value')
result2 = pd.concat([d1, df1], axis=1)
10 loops, best of 3: 77.2 ms per loop
大约1/3的速度提升来自使用strip
而不是replace
,而使用pivot
代替unstack
则是2/3。 (无论如何,concat
步骤是相同且非常快的。)
答案 2 :(得分:2)
查看最终数据框的创建方式,字符串列的单热编码在与其他数据框相比时的整体性能方面确实看起来不是一个坏主意迄今提到的方法。
<强> 步骤: 强>
在pd.get_dummies
系列上使用metric_type
,从分类变量中创建虚拟变量。这一部分加上str.strip
是该批次中最耗时的部分。
不是直接在系列对象上剥离前导/尾随空白字符,而是计算get_dummies
部分,因为很有可能在系列中重复了一些分类变量稍后将在虚拟创建期间共享相同的列。重复变量越多,过滤掉这些额外空间所花费的时间就越少。仅对获取的虚拟变量str.strip
的列执行DF
。这种方法节省了大量时间。
DF
。np.unique
参数的return_index=True
来提取存在的唯一列以及相应的索引。np.add.reduceat
,其工作方式类似于groupby
操作(相当于 - df.groupby(df.columns.tolist(), axis=1).sum()
),但具有真正快速的专长。要配对的索引由idx
np.unique
提供。值的减少发生在这些切片上,并且它们的运行总和在列(axis=1
)之间计算。 dtype
返回bool
,这有助于我们使用np.where
,因为它的功能类似于布尔掩码,其中1&#39; s / 0被映射到{{分别是1}} / True
。然后,这些1由False
系列中的值和metric_value
的0来填充。NaN
已准备就绪,需要与原始的DF
列开始连接,从而产生最终清理的数据框。解决方案:
DF
<强> 时序: 强>
def dummies_strip_concat(df):
one_hot_enc = pd.get_dummies(df.metric_type)
one_hot_enc.columns = one_hot_enc.columns.str.strip()
one_hot_enc.sortlevel(axis=1, inplace=True)
a, idx = np.unique(one_hot_enc.columns.values, return_index=True)
out = np.where(np.add.reduceat(one_hot_enc.values, idx, axis=1, dtype=np.bool),
df.metric_value.values[:, None],
np.nan)
return (pd.concat([pd.DataFrame(out, df.index, a), df], axis=1))
对于包含几千行的def pir(df):
idx = df.metric_type.str.replace(' ', '')
d1 = df.set_index(idx, append=True).metric_value.unstack()
return pd.concat([d1, df], axis=1)
def johne(df):
df.metric_type = df.metric_type.str.strip()
d1 = df.pivot(columns='metric_type', values='metric_value')
return pd.concat([d1, df], axis=1)
,与OP的想法相比:
DF