Python Pandas Column(存储集)与字典集和返回数字

时间:2017-01-06 22:51:09

标签: python pandas dictionary set

在Pandas数据框中列出超过15条MM的记录,并尝试确定该字段中包含的唯一有效英文单词的数量。

我怎样才能加快速度?比较。我使用的是set.intersect(..),但它花了一个多小时。代码和示例数据如下。

df.info()
sys_id      float64
grp_id      float64
set_id      float64
desc        object
unique_set  object

这些记录的唯一关键是" * id"领域。 Desc是用户定义的描述

我使用以下代码创建unique_set:

df['unique_set'] = df.data_desc.apply(lambda x: detSet(x))

定义detSet的地方:

def detSet(strDesc):
    if len(str(strDesc)) <= 1:
        # Return an empty set
        return set()
    else:
        # Remove all punctation
        strDesc = strDesc.translate(replace_punctuation).lower()
        # Remove all Non Alphabetic Characters (including Numbers)
        strDesc = re.sub(r'[^a-zA-Z ]', ' ', strDesc)
        # Remove all words less than 4 characters long
        strDesc = re.sub(r'\b\w{1,3}\b','', strDesc)
        # Remove all the extra spaces
        strDesc = ' '.join(strDesc.split())
        #
        return set(strDesc.split())

然后我从http://invpy.com/dictionary.txt

中读了一份英语词典
ENGLISH_WORDS = open('Dictionary.txt').read().splitlines()
ENGLISH_WORDS = [e.lower() for e in ENGLISH_WORDS]

df['num_english'] = df.unique_set.apply(lambda x: detNumEnglish(x)).astype(np.int16)

def detNumEnglish(setDesc):
    if len(setDesc) == 0:
        return -1
    else:
        return len(setDesc.intersection(ENGLISH_WORDS))

一些示例数据:

141   9437  13522   {jelly, beans, pudding, cake, fruitc}
787   29575 5915    {ingerbread, sugar, plum, powder, jelly}
842   22909 28065   {pudding, bear, claw, sesame, snaps, m}
484   36065 25069   {isu, cake, candy, canes, ca}
897   54587 48574   {tart, fruitcake, dessert, bisc}
123   48335 36038   {chocolate, icing, marzipan, macaroon, apple}
293   36779 12239   {ars, sugar, plum, cupcake, danish, tiramis}
115   18478 43114   {e, pudding, gummies, chocola}
183   13346 33084   {roll, caramels, candy, fruitcak}
501   94397 47227   {cake, candy, canes, cake}
473   52269 44396   {e, gummi, bears, tiramisu, cake, candy}

1 个答案:

答案 0 :(得分:0)

首先,您应该将ENGLISH_WORDS创建为set以加快交叉点并删除短文字,因为它们会从您的其他列表中过滤掉:

ENGLISH_WORDS = {e.lower() for e in ENGLISH_WORDS if len(e)>3}

然后,看起来detSet方法被大量调用,并且它执行了大量的字符串操作,这是很昂贵的。

让我们看一下您原来的片段:

def detSet(strDesc):
    ...
    else:
        # Remove all punctation
        strDesc = strDesc.translate(replace_punctuation).lower()
        # Remove all Non Alphabetic Characters (including Numbers)
        strDesc = re.sub(r'[^a-zA-Z ]', ' ', strDesc)

(这是您的原始代码段)

上面两行基本上都做同样的事情。您只需要通过执行以下操作添加数字的翻译(以及可能需要改进的其他字符):

replace_punctuation.update({i:' ' for i in range(ord('0'),ord('9'))})

创建replace_punctuation表时。所以你可以放弃代价高昂的正则表达式。

完成后,您已经有了一串空格分隔的单词。此时您应该拆分并过滤掉小字。以下3行:

    # Remove all words less than 4 characters long
    strDesc = re.sub(r'\b\w{1,3}\b','', strDesc)
    # Remove all the extra spaces
    strDesc = ' '.join(strDesc.split())
    #
    return set(strDesc.split())

可以直接写成一个集合理解(1个或多个空格:如果没有任何参数调用split也没关系)所以你不创建临时列表(你在代码中创建了很多)

return {s for s in strDesc.split() if len(s) > 4}

这应该快得多,因为字符串和正则表达式操作要少得多。

总而言之,这就是加速程序的样子:

def detSet(strDesc):
    if len(str(strDesc)) <= 1:
        # Return an empty set
        return set()
    else:
        # Remove all punctation & replace digits by spaces (using the new replace_punctuation table)
        strDesc = strDesc.translate(replace_punctuation).lower()
        # split, filter out small words, and create set comprehension directly
        return {s for s in strDesc.split() if len(s) > 4}

除此之外lambda有点矫枉过正:

df.data_desc.apply(lambda x: detSet(x))

可能很简单(也稍快):

df.data_desc.apply(detSet)

(同样适用于df.unique_set.apply(lambda x: detNumEnglish(x))