首页 > 解决方案 > python中有没有更快的方法将字符串拆分为具有100万个元素的列表中的子列表?

问题描述

我正在尝试帮助我的朋友清理包含一百万个元素的订单列表数据框。

在此处输入图像描述

您可以看到 product_name 列应该是一个列表,但它们是字符串类型。所以我想把它们分成子列表。

这是我的代码:</p>

order_ls = raw_df['product_name'].tolist()
cln_order_ls = list()
for i in order_ls:
    i = i.replace('[', '')
    i = i.replace(']', '')
    i = i.replace('\'', '')
    cln_order_ls.append(i)

new_cln_order_ls = list()
for i in cln_order_ls:
    new_cln_order_ls.append(i.split(', '))

但在“拆分”部分,处理需要大量时间。我想知道有没有更快的方法来处理它?<​​/p>

谢谢~

标签: pythonpython-3.xpandasperformancedataframe

解决方案


编辑

(我不喜欢最后一个答案,它太混乱了,所以我重新排序并更系统地测试了我)。

长话短说:

为了速度,只需使用:

def str_to_list(s):
    return s[1:-1].replace('\'', '').split(', ')


df['product_name'].apply(str_to_list).to_list()

长话短说:

让我们剖析您的代码:

order_ls = raw_df['product_name'].tolist()
cln_order_ls = list()
for i in order_ls:
    i = i.replace('[', '')
    i = i.replace(']', '')
    i = i.replace('\'', '')
    cln_order_ls.append(i)

new_cln_order_ls = list()
for i in cln_order_ls:
    new_cln_order_ls.append(i.split(', '))

您真正想做的是拥有一个功能,例如str_to_list()将您的输入转换strlist.

由于某些原因,您需要分多个步骤执行此操作,但这实际上没有必要。到目前为止,您可以将其重写为:

def str_to_list_OP(s):
    return s.replace('[', '').replace(']', '').replace('\'', '').split(', ')

如果您可以假设[并且]始终是字符串的第一个和最后一个字符,则可以将其简化为:

def str_to_list(s):
    return s[1:-1].replace('\'', '').split(', ')

这也应该更快。

替代方法将使用正则表达式,例如:

def str_to_list_regex(s):
    regex = re.compile(r'[\[\]\']')
    return re.sub(regex, '', s).split(', ')

请注意,到目前为止,所有方法都使用split(). 这是一个相当快的实现,它接近C 的速度,几乎没有任何 Python 构造可以击败它。

所有这些方法都非常不安全,因为它们没有正确考虑转义,例如,对于以下有效的 Python 代码,上述所有方法都将失败:

['ciao', "pippo", 'foo, bar']

在这种情况下,更强大的替代方案是:

  1. ast.literal_eval适用于任何有效的 Python 代码
  2. json.loads这实际上需要有效的 JSON 字符串,所以这里并不是一个真正的选择。

此处比较了这些解决方案的速度:

基准1

如您所见,安全是以速度为代价的。

(这些图表是使用这些脚本生成的,具有以下内容

def gen_input(n):
    return str([str(x) for x in range(n)])


def equal_output(a, b):
    return a == b


input_sizes = (5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000)  
funcs = str_to_list_OP, str_to_list, str_to_list_regex, ast.literal_eval 


runtimes, input_sizes, labels, results = benchmark(
    funcs, gen_input=gen_input, equal_output=equal_output,
    input_sizes=input_sizes)

现在让我们专注于循环。您所做的是显式循环,我们知道 Python 通常不会非常快。但是,在推导中循环可以更快,因为它可以生成更优化的代码。另一种方法是使用使用 Pandas 原语的矢量化表达式,使用apply()或使用.str.链接。

获得了以下时间,表明对于较小的输入,理解是最快的,尽管矢量化解决方案(使用apply)赶上并最终超过了理解:

基准2

使用了以下测试函数:

import pandas as pd


def str_to_list(s):
    return s[1:-1].replace('\'', '').split(', ')


def func_OP(df):
    order_ls = df['product_name'].tolist()
    cln_order_ls = list()
    for i in order_ls:
        i = i.replace('[', '')
        i = i.replace(']', '')
        i = i.replace('\'', '')
        cln_order_ls.append(i)
    new_cln_order_ls = list()
    for i in cln_order_ls:
        new_cln_order_ls.append(i.split(', '))
    return new_cln_order_ls


def func_QuangHoang(df):
    return df['product_name'].str[1:-1].str.replace('\'','').str.split(', ').to_list()


def func_apply_df(df):
    return df['product_name'].apply(str_to_list).to_list()
    
    
def func_compr(df):
    return [str_to_list(s) for s in df['product_name']]

使用以下测试代码:

def gen_input(n):
    return pd.DataFrame(
        columns=('order_id', 'product_name'),
        data=[[i, "['ciao', 'pippo', 'foo', 'bar', 'baz']"] for i in range(n)])


def equal_output(a, b):
    return a == b


input_sizes = (5, 10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000)  
funcs = func_OP, func_QuangHoang, func_apply_df, func_compr 


runtimes, input_sizes, labels, results = benchmark(
    funcs, gen_input=gen_input, equal_output=equal_output,
    input_sizes=input_sizes)

再次使用与以前相同的基本脚本


推荐阅读