将函数应用于pandas

时间:2017-07-06 03:01:40

标签: python pandas dataframe

是否可以将函数应用于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

2 个答案:

答案 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