金鞍玉勒寻芳客,未信我庐别有春。这篇文章主要讲述从pandas apply()返回多列。相关的知识,希望能为你提供帮助。
我有一个pandas DataFrame。df_test
.它包含了一列'size',代表着以字节为单位的大小。 我用下面的代码计算了KB、MB和GB。
df_test = pd.DataFrame([
{'dir': '/Users/uname1', 'size': 994933},
{'dir': '/Users/uname2', 'size': 109338711},
])df_test['size_kb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0, grouping=True) + ' KB')
df_test['size_mb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 2, grouping=True) + ' MB')
df_test['size_gb'] = df_test['size'].astype(int).apply(lambda x: locale.format("%.1f", x / 1024.0 ** 3, grouping=True) + ' GB')df_testdirsizesize_kbsize_mb size_gb
0/Users/uname1994933971.6 KB0.9 MB0.0 GB
1/Users/uname2109338711106,776.1 KB104.3 MB0.1 GB[2 rows x 5 columns]
我已经运行了12万行,根据%timeit,每列需要2. 97秒*3=约9秒。
有什么办法可以让这个过程更快吗? 例如,我可以不从apply一次返回一列并运行3次,而是一次返回所有三列并插入到原始数据框架中吗?
我找到的其他问题都是想要 取多个值并返回一个值. 我想... 取单列值并返回多列.
答案这是一个老问题,但为了完整起见,你可以从应用的函数中返回一个包含新数据的Series,以防止需要迭代三次。 传递
axis=1
的应用函数应用该函数 sizes
到数据框架的每一行,返回一个系列,添加到一个新的数据框架。 这个系列,s,包含新的值,以及原始数据。def sizes(s):
s['size_kb'] = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
s['size_mb'] = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
s['size_gb'] = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
return sdf_test = df_test.append(rows_list)
df_test = df_test.apply(sizes, axis=1)
另一答案使用apply和zip会比Series方式快3倍。
def sizes(s):
return locale.format("%.1f", s / 1024.0, grouping=True) + ' KB',
locale.format("%.1f", s / 1024.0 ** 2, grouping=True) + ' MB',
locale.format("%.1f", s / 1024.0 ** 3, grouping=True) + ' GB'
df_test['size_kb'],df_test['size_mb'], df_test['size_gb'] = zip(*df_test['size'].apply(sizes))
测试结果是。
Separate df.apply(): 100 loops, best of 3: 1.43 ms per loopReturn Series: 100 loops, best of 3: 2.61 ms per loopReturn tuple:1000 loops, best of 3: 819 μs per loop
另一答案目前的一些回复都很好,但我想提供另一个,也许更 "平铺直叙 "的选项。对我来说,这工作与当前的 大熊猫 0.23 (不知道在以前的版本中是否能用)。
import pandas as pddf_test = pd.DataFrame([
{'dir': '/Users/uname1', 'size': 994933},
{'dir': '/Users/uname2', 'size': 109338711},
])def sizes(s):
a = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
b = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
c = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
return a, b, cdf_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes, axis=1, result_type="expand")
注意,这个技巧是在
result_type
的参数 apply
,将其结果扩展为 DataFrame
可以直接赋值给新旧列。另一答案只是另一种可读的方式。这段代码将增加三个新的列和它的值,在应用函数中不使用参数就返回系列。
def sizes(s):val_kb = locale.format("%.1f", s['size'] / 1024.0, grouping=True) + ' KB'
val_mb = locale.format("%.1f", s['size'] / 1024.0 ** 2, grouping=True) + ' MB'
val_gb = locale.format("%.1f", s['size'] / 1024.0 ** 3, grouping=True) + ' GB'
return pd.Series([val_kb,val_mb,val_gb],index=['size_kb','size_mb','size_gb'])df[['size_kb','size_mb','size_gb']] = df.apply(lambda x: sizes(x) , axis=1)
一个一般的例子来自。https:/pandas.pydata.orgpandas-docsstablegeneratedpandas.DataFrame.apply.html。
df.apply(lambda x: pd.Series([1, 2], index=['foo', 'bar']), axis=1)#foobar
#012
#112
#212
另一答案真的很酷的答案!谢谢杰西和jaumebonet。谢谢杰西和jaumebonet! 只是一些观察,在关于。
zip(* ...
... result_type="expand")
import pandas as pddat = [ [i, 10*i] for i in range(1000)]df = pd.DataFrame(dat, columns = ["a","b"])def add_and_sub(row):
add = row["a"] + row["b"]
sub = row["a"] - row["b"]
return add, subdf[["add", "sub"]] = df.apply(add_and_sub, axis=1, result_type="expand")
# versus
df["add"], df["sub"] = zip(*df.apply(add_and_sub, axis=1))
另一答案顶级答案之间的性能差异很大,Jesse & famaral42已经讨论过这个问题,但值得分享一下顶级答案之间的公平比较,并阐述Jesse答案中一个微妙但重要的细节。传入函数的参数,也会影响性能。.
(python 3.7.4, Pandas 1.0.3)
import pandas as pd
import locale
import timeitdef create_new_df_test():
df_test = pd.DataFrame([
{'dir': '/Users/uname1', 'size': 994933},
{'dir': '/Users/uname2', 'size': 109338711},
])
return df_testdef sizes_pass_series_return_series(series):
series['size_kb'] = locale.format_string("%.1f", series['size'] / 1024.0, grouping=True) + ' KB'
series['size_mb'] = locale.format_string("%.1f", series['size'] / 1024.0 ** 2, grouping=True) + ' MB'
series['size_gb'] = locale.format_string("%.1f", series['size'] / 1024.0 ** 3, grouping=True) + ' GB'
return seriesdef sizes_pass_series_return_tuple(series):
a = locale.format_string("%.1f", series['size'] / 1024.0, grouping=True) + ' KB'
b = locale.format_string("%.1f", series['size'] / 1024.0 ** 2, grouping=True) + ' MB'
c = locale.format_string("%.1f", series['size'] / 1024.0 ** 3, grouping=True) + ' GB'
return a, b, cdef sizes_pass_value_return_tuple(value):
a = locale.format_string("%.1f", value / 1024.0, grouping=True) + ' KB'
b = locale.format_string("%.1f", value / 1024.0 ** 2, grouping=True) + ' MB'
c = locale.format_string("%.1f", value / 1024.0 ** 3, grouping=True) + ' GB'
return a, b, c
下面是结果。
# 1 - Accepted (Nels11 Answer) - (pass series, return series):
9.82 ms ± 377 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)# 2 - Pandafied (jaumebonet Answer) - (pass series, return tuple):
2.34 ms ± 48.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)# 3 - Tuples (pass series, return tuple then zip):
1.36 ms ± 62.8 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)# 4 - Tuples (Jesse Answer) - (pass value, return tuple then zip):
752 μs ± 18.5 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
请注意,返回tuple是最快的方法,但传递的是什么?在 作为一个参数,也会影响性能。代码中的差异是微妙的,但性能的提升是显著的。
测试#4(传递单个值)的速度是测试#3(传递一系列值)的两倍,尽管执行的操作表面上是相同的。
但还有更多...
# 1a - Accepted (Nels11 Answer) - (pass series, return series, new columns exist):
3.23 ms ± 141 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)# 2a - Pandafied (jaumebonet Answer) - (pass series, return tuple, new columns exist):
2.31 ms ± 39.3 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)# 3a - Tuples (pass series, return tuple then zip, new columns exist):
1.36 ms ± 58.4 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)# 4a - Tuples (Jesse Answer) - (pass value, return tuple then zip, new columns exist):
694 μs ± 3.9 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
在某些情况下(#1a和#4a),将函数应用到输出列已经存在的DataFrame中比从函数中创建它们要快。
【从pandas apply()返回多列。】下面是运行测试的代码。
# Paste and run the following in ipython console. It will not work if you run it from a .py file.
print('
Accepted Answer (pass series, return series, new columns dont exist):')
df_test = create_new_df_test()
%timeit result = df_test.apply(sizes_pass_series_return_series, axis=1)
print('Accepted Answer (pass series, return series, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit result = df_test.apply(sizes_pass_series_return_series, axis=1)print('
Pandafied (pass series, return tuple, new columns dont exist):')
df_test = create_new_df_test()
%timeit df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes_pass_series_return_tuple, axis=1, result_type="expand")
print('Pandafied (pass series, return tuple, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb', 'size_gb'])])
%timeit df_test[['size_kb', 'size_mb', 'size_gb']] = df_test.apply(sizes_pass_series_return_tuple, axis=1, result_type="expand")print('
Tuples (pass series, return tuple then zip, new columns dont exist):')
df_test = create_new_df_test()
%timeit df_test['size_kb'],df_test['size_mb'], df_test['size_gb'] = zip(*df_test.apply(sizes_pass_series_return_tuple, axis=1))
print('Tuples (pass series, return tuple then zip, new columns exist):')
df_test = create_new_df_test()
df_test = pd.concat([df_test, pd.DataFrame(columns=['size_kb', 'size_mb',
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 如何在改变sharedpreferences数据后自动改变android menuItem数据()
- RenderScript在Android 6上崩溃
- 如何在OncreateMethod中同时使用AppCompatActivity和Activity类[重复]。
- 我可以在Aws AppSync解析器映射模板中对用户输入进行消毒吗()
- 在安卓系统中,我不能在viewholder中设置图片。
- Android FusedLocationProviderClient.requestLocationUpdates()返回相同的位置坐标。
- 无法获得Android系统中googlemap V2上标记的自定义信息窗口的点击事件。
- 在AppleScript中,在标记后面取X符号并保存到变量中。
- 希望对Android应用中的Kotlin非空断言错误进行解释。