处理数百万条推文 - 循环非常慢。关于如何加快速度的任何想法?

时间:2017-04-17 17:44:04

标签: python python-3.x pandas twitter data-processing

正如标题所示,我正在处理数百万条推文,其中一个数据点是两个不同的列表中是否存在任何单词(每个列表包含大约500个单词)。这可以理解的很慢,但我会定期这样做,所以我想加快速度。关于我怎么可能的任何想法?

lista = ['word1', 'word2', ... 'word500']
listb = ['word1', 'word2', ..., 'word500']

def token_list_count(df):

    for i, t in df.iterrows():

        list_a = 0
        list_b = 0

        for tok in t['tokens']:
            if tok in lista: list_a += 1
            elif tok in listb: list_b += 1

        df.loc[i, 'token_count'] = int(len(t['tokens']))
        df.loc[i, 'lista_count'] = int(list_a)
        df.loc[i, 'listb_count'] = int(list_b)

        if i % 25000 == 0: print('25k more processed...')

    return df

编辑:

输入/之前:

enter image description here

输出/之后:

enter image description here

2 个答案:

答案 0 :(得分:3)

为了避免迭代数据帧,您可以尝试以下函数。第一行将tokens列扩展为数据框,其中每个第一个标记将位于第一列中,第二个标记位于第二列中,依此类推。列总数将等于token_count中的最大df。从那里可以很容易地使用lista方法计算listb.isin计数,并对每行进行求和。

从完整tok制作的df数据框可能占用大量内存,在这种情况下,可能需要将df拆分为多个块并单独处理它们。

def token_list_count(df):
    tok = df['tokens'].apply(pd.Series)
    df['token_count'] = tok.notnull().sum(axis=1)
    df['lista_count'] = tok.isin(lista).sum(axis=1)
    df['listb_count'] = tok.isin(listb).sum(axis=1)
    return df

您的原始功能也有一些改进空间:

  • 没有必要使用df迭代完整iterrows(),因为您只需要一列来进行计算。因此,您可以使用df.tokens.iteritems()代替。这将节省您在每次迭代时创建一个新的Series对象。
  • 由于您一次只能设置一个新值,因此您可以使用at索引器而不是loc。请参阅pandas docs中的Fast scalar value getting and setting

我不知道这有多大帮助,但可能有些=)。

def token_list_count(df):
    for i, tok in df.tokens.iteritems():
        list_a = sum((t in lista) for t in tok)
        list_b = sum((t in listb) for t in tok)

        df.at[i, 'token_count'] = int(len(tok))
        df.at[i, 'lista_count'] = int(list_a)
        df.at[i, 'listb_count'] = int(list_b)

        if i % 25000 == 0: print('25k more processed...')
    return df

你也可以在没有for循环但是使用apply

的情况下编写它
df['token_count'] = df.tokens.apply(len)
df['lista_count'] = df.tokens.apply(lambda tok: sum((t in lista) for t in tok))
df['listb_count'] = df.tokens.apply(lambda tok: sum((t in listb) for t in tok))

答案 1 :(得分:2)

使用下面的代码可以给你一些小时间优势。请注意,如果您按原样使用此代码,则listb中的出现次数不正确。

你不会从下面的代码中获得更多的加速,因为创建这些集将消除几乎所有来自更快查找的时间优势,但是如果你重用seta你会节省时间,setb也可以用于另一个查找循环如果进一步编码。

也许您可以安排,直接将您的列表作为集合,这样您可以节省创建集合的时间吗?

lista = ['word1a', 'word2a', 'word500a']
listb = ['word1b', 'word2a', 'word500b']

seta = set(lista)
setb = set(listb)

def token_list_count(df):

    dfIterrows = df.iterrows() # TRY THIS !!! 
    for i, t in dfIterrows:    # TRY THIS !!! 
        list_a = 0
        list_b = 0
        tTokens = t['tokens']  # TRY THIS !!!
        for tok in tTokens:    # TRY THIS !!!
            if   tok in seta: list_a += 1
            elif tok in setb: list_b += 1
            # why not "if tok in setb: list_b +=1" ???
        df.loc[i, 'token_count'] = int(len(t['tokens']))
        df.loc[i, 'lista_count'] = int(list_a)
        df.loc[i, 'listb_count'] = int(list_b)

        if i % 25000 == 0: print('25k more processed...')

    return df

如果速度对你很重要,我建议你只使用Python作为框架,使用适当的可执行文件或数据库查询进行查找,以及用于seta和setb中的查找的多处理。< / p>

另一个选择是考虑评论中建议的root:&#34;&#34;&#34;这里最大的问题是你在一个DataFrame上进行迭代,这几乎应该是永远避免。使用矢量化方法将产生最佳性能改进。 &#34;&#34;&#34;