python pandas dataframe:将函数返回元组分配给数据框的两列

Posted

技术标签:

【中文标题】python pandas dataframe:将函数返回元组分配给数据框的两列【英文标题】:python pandas data frame: assign function return tuple to two columns of a data frame 【发布时间】:2021-06-23 15:19:05 【问题描述】:

我想使用返回 tuple 的函数向 pandas Dataframe 添加两列:

data=pd.DataFrame('a':[1,2,3,4,5,6],'b':['ssdfsdf','bbbbbb','cccccccccccc','ddd','eeeeee','ffffff'])

def givetup(string):
    
    result1 = string[0:3]
    # please imagine here a bunch of string functions concatenated.
    # including nlp methods with SpaCy 
    result2 = result1.upper()
    # the same here, imagine a bunch of steps to calculate result2 based on result 1
    
    return (result1,result2)

data['c'] = data['b'].apply(lambda x: givetup(x)[0])
data['d'] = data['b'].apply(lambda x: givetup(x)[1])

这是非常低效的(我正在处理数百万行),因为我调用了两次相同的函数并进行了两次计算。 由于result2 依赖于result 1 我最好不要将givetup 分成两个函数 如何只调用一次函数就将 result1result2 一次性分配到新列 cd 中? 最有效的方法是什么?

请记住,result1result2 是非常耗时的字符串计算。

编辑 1: 我知道这件事: Apply pandas function to column to create multiple new columns?

即应用矢量化函数。在我的特殊情况下,这是非常不可取的,甚至是不可能的。想象一下,结果 1 和结果 2 是根据语言模型计算出来的,我需要纯文本。

【问题讨论】:

result2 取决于结果 1 是否可以编写两个(矢量化)函数,一个获取result1,一个分别获取result2。那你可以data['c'] = func1(data['b']); data['d'] = func2(data['c'])? 跟进@QuangHoang 所说的话。我像这样矢量化data.assign(c=lambda d: d.b.str[0:3], d=lambda d: d.c.str.upper()) 【参考方案1】:

您可以在这里尝试列表理解:

data[['c','d']] = [givetup(a) for a in data['b']]

输出:

   a             b    c    d
0  1       ssdfsdf  ssd  SSD
1  2        bbbbbb  bbb  BBB
2  3  cccccccccccc  ccc  CCC
3  4           ddd  ddd  DDD
4  5        eeeeee  eee  EEE
5  6        ffffff  fff  FFF

【讨论】:

【参考方案2】:

zip/map

data['c'], data['d'] = zip(*map(givetup, data['b']))

data

   a             b    c    d
0  1       ssdfsdf  ssd  SSD
1  2        bbbbbb  bbb  BBB
2  3  cccccccccccc  ccc  CCC
3  4           ddd  ddd  DDD
4  5        eeeeee  eee  EEE
5  6        ffffff  fff  FFF

Series.strassign

这是特定于givetup 中给出的示例。但如果可以解开,那可能是值得的。

assign 方法参数可以采用引用在参数之前创建的列 (NEAT) 的 calables。

data.assign(c=lambda d: d.b.str[0:3], d=lambda d: d.c.str.upper())

   a             b    c    d
0  1       ssdfsdf  ssd  SSD
1  2        bbbbbb  bbb  BBB
2  3  cccccccccccc  ccc  CCC
3  4           ddd  ddd  DDD
4  5        eeeeee  eee  EEE
5  6        ffffff  fff  FFF

时间

data = pd.concat([data] * 10_000, ignore_index=True)

%timeit data['c'], data['d'] = zip(*map(givetup, data['b']))
%timeit data[['c','d']] = [givetup(a) for a in data['b']]
%timeit data.assign(c=lambda d: d.b.str[0:3], d=lambda d: d.c.str.upper())

69.7 ms ± 865 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
137 ms ± 937 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
34.6 ms ± 235 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

【讨论】:

似乎第二个是要走的路。在我的特殊情况下,第三个是不可能的 这里的第一个和第二个解决方案给出:/opt/conda/lib/python3.8/site-packages/numpy/core/_asarray.py:102: VisibleDeprecationWarning: Creating an ndarray from ragged nested不推荐使用序列(它是具有不同长度或形状的列表或元组或 ndarray 的列表或元组)。如果您打算这样做,则必须在创建 ndarray 时指定“dtype=object”。 return array(a, dtype, copy=False, order=order) 是什么意思? 这意味着您的元组不是长度为 2 的玩具示例不代表您的实际情况。 您可以这样做以确保您始终只获得两个,data['c'], data['d'] = zip(*[x[:2] for x in map(givetup, data['b'])])【参考方案3】:

另一种方法是对系列使用 apply 函数:

import pandas as pd

data=pd.DataFrame('a':[1,2,3,4,5,6],'b':['ssdfsdf','bbbbbb','cccccccccccc','ddd','eeeeee','ffffff'])

def givetup(column):
    
    column1 = column[0:3]
    column2 = column[0:3].upper()
    
    return pd.Series([column1, column2])

data[['c','d']] = data['b'].apply(lambda x: givetup(x))

【讨论】:

这种方法可能效率很低。首先,您为每一行创建一个pd.Series。然后你要求 Pandas 为每一行对齐这些新列。此外,column1 = column[0:3] 已经对字符串进行了切片。当您column2 = column[0:3].upper() 时,您再次执行此操作,这样做是浪费每一行。这样做的时间要慢 1000 倍。

以上是关于python pandas dataframe:将函数返回元组分配给数据框的两列的主要内容,如果未能解决你的问题,请参考以下文章

python 将Numpy数组转换为Pandas Dataframe

python pandas中如何将dataframe中的一列字符串类型转换为浮点类型?

python 将Pandas Dataframe导出到csv(无索引)

python 将Pandas Dataframe导出到Excel文件中

python Pandas - 将系列转换为DataFrame,顶部有列

Python/Pandas:如何将字符串列表与 DataFrame 列匹配