是否可以将函数应用于pandas中的DataFrame 中的每个单元格?
我知道pandas.DataFrame.applymap但它似乎不允许应用程序:
import numpy as np
import pandas as pd
np.random.seed(1)
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(frame)
format = lambda x: '%.2f' % x
frame = frame.applymap(format)
print(frame)
返回:
b d e
Utah 1.624345 -0.611756 -0.528172
Ohio -1.072969 0.865408 -2.301539
Texas 1.744812 -0.761207 0.319039
Oregon -0.249370 1.462108 -2.060141
b d e
Utah 1.62 -0.61 -0.53
Ohio -1.07 0.87 -2.30
Texas 1.74 -0.76 0.32
Oregon -0.25 1.46 -2.06
frame = frame.applymap(format)
将暂时保留2份frame
个
记忆,我不想要。
我知道可以使用NumPy数组向每个单元格应用函数:Mapping a NumPy array in place。
答案 0 :(得分:1)
如果对您来说很重要,您可以尝试制作自己的cpython功能
我找到了applymap function in pandas
def applymap(self, func):
# ...
def infer(x):
if x.empty:
return lib.map_infer(x, func)
return lib.map_infer(x.asobject, func)
return self.apply(infer)
表明lib.map_infer
正在幕后工作
lib.map_infer
是一个定义为here的cython方法,您可以清楚地看到它们为新结果分配空间:result = np.empty(n, dtype=object)
,位于
def map_infer(ndarray arr, object f, bint convert=1):
"""
Substitute for np.vectorize with pandas-friendly dtype inference
Parameters
----------
arr : ndarray
f : function
Returns
-------
mapped : ndarray
"""
cdef:
Py_ssize_t i, n
ndarray[object] result
object val
n = len(arr)
result = np.empty(n, dtype=object)
for i in range(n):
val = f(util.get_value_at(arr, i))
# unbox 0-dim arrays, GH #690
if is_array(val) and PyArray_NDIM(val) == 0:
# is there a faster way to unbox?
val = val.item()
result[i] = val
if convert:
return maybe_convert_objects(result,
try_float=0,
convert_datetime=0,
convert_timedelta=0)
return result
......这是我答案的悬崖机库。也许op或某人可以修改cython方法来创建一个修改原始数组而不是创建新结果的inplace版本
- 我现在远离我的编码计算机,所以我无法测试任何东西:(
答案 1 :(得分:1)
如果我的理解是正确的,那么大熊猫就地操作涉及调用.update_inplace()
方法,例如,.replace()
会首先计算新的替换数据,然后相应地更新数据框。
.applymap()
是.apply()
的包装器;这些都没有提供inplace选项,但即使他们这样做,他们仍然需要在修改数据帧之前将所有输出数据存储在内存中。
从来源.applymap()
调用.apply()
,调用.aggregate()
,调用_aggregate()
,调用._agg()
,这只是一个for {循环运行在Python (即不是Cython - 我认为)。
您当然可以直接修改基础NumPy数组:以下代码对数据框进行了四舍五入:
frame = pd.DataFrame(np.random.randn(100, 100))
for i in frame.index:
for j in frame.columns:
val = round(frame.values[i,j])
frame.values[i,j] = val
newvals = np.zeros(frame.shape[1])
for i in frame.index:
for j in frame.columns:
val = round(frame.values[i,j])
newvals[j] = val
frame.values[i] = newvals
第一种方法一次设置一个元素,大约需要1秒,第二个按行设置,需要100ms; .applymap(round)
在20ms内完成。
然而,有趣的是,如果我们使用frame = pd.DataFrame(np.random.randn(1, 10000))
,第一种方法和.applymap(round)
都需要大约1.2秒,第二种方法大约需要100毫秒。
最后,frame = pd.DataFrame(np.random.randn(10000,1))
的第一种和第二种方法采用1(不出所料),.applymap(round)
采用10毫秒。
这些结果或多或少表明 .applymap
基本上是在每列上迭代。
我尝试使用3种不同形状运行frame.applymap(round)
:( 10000,1),(100,100)和(1,10000)。第一个是最快的,第三个是最慢的;这表明.applymap()
遍历列。以下代码与.applymap()
的内容大致相同:
newvals = np.zeros(frame.shape[1])
for i in frame.index:
for j in frame.columns:
val = round(frame.values[i,j])
newvals[j] = val
frame.values[i] = newvals
这个使用底层NumPy数组的副本:
newvals = np.zeros(frame.shape[1])
arr = frame.values
for i in frame.index:
for j in frame.columns:
val = round(arr[i,j])
newvals[j] = val
arr[i] = newvals
使用100x100数据帧,前者花了大约300ms来运行,后者花了60ms - 差别仅仅是因为必须访问数据帧中的.values
!
在Cython中运行后者需要大约34毫秒,而.applymap(round)
在24毫秒内执行。我不知道为什么.applymap()
在这里仍然更快。
回答这个问题:可能没有.applymap()
的就地实施;如果存在,则很可能涉及在进行就地更改之前存储所有“应用”值。
如果你想在原地进行.applymap()
,你可以迭代底层的NumPy数组。但是,这会带来性能损失 - 最佳解决方案可能会在行或列上进行迭代:例如分配arr=df.values[i]
,对arr
的每个元素应用函数,按df.values[i] = arr
修改数据框,并迭代所有i
。