【问题标题】:Pandas dataframe.loc method too slowPandas dataframe.loc 方法太慢
【发布时间】:2018-08-01 11:10:30
【问题描述】:

我有一个这样的 +100K 行的数据框:

   user  document
0  john      book
1  jane   article
2  jane      book
3  jane      book
4   jim   article
5  john      book
6   jim  blogpost
7  jane  blogpost
8  jane  blogpost
9  jane  blogpost

我需要这样的数据框:

      blogpost  article  book
john         1        3     0
jane         0        0     1
jim          4        0     2

也就是说,我需要每个user, document 组合的下载次数。

我在做.groupby(['user', 'document'])然后用df.loc设置下载号:

df = pd.DataFrame(index=users, columns=documents)
df.fillna(0, inplace=True)

grouped = records.groupby(['user', 'document'])
for elem in grouped:
    user, document = elem[0]
    downloads = len(elem[1])
    df.loc[user, document] = downloads

但是,df.loc 以这种方式使用时非常慢...我已经注释掉了 df.loc.. 行,发现循环完成得很快,所以几乎可以肯定是 df.loc 访问速度很慢。

我怎样才能更快地得到这个结果?

最小工作示例:

records = pd.DataFrame([
    ('john', 'book'), 
    ('jane', 'article'),
    ('jane','book'),
    ('jane','book'),
    ('jim', 'article'), 
    ('john', 'book'),
    ('jim', 'blogpost'), 
    ('jane', 'blogpost'),
    ('jane', 'blogpost'),
    ('jane', 'blogpost')
    ], columns=['user', 'document'])
print(records)

users = list(set(records['user']))
users.sort()
documents = list(set(records['document']))
documents.sort()

print(users)
print(documents)

df = pd.DataFrame(index=users, columns=documents)
df.fillna(0, inplace=True)
print(df)

grouped = records.groupby(['user', 'document'])
for elem in grouped:
    user, document = elem[0]
    downloads = len(elem[1])
    df.loc[user, document] = downloads

【问题讨论】:

  • 我也经历过同样的事情。它确实很慢。相反,我在循环之外定义了 empy 列表,并将数据附加到列表中,并将 df 的特定列添加到这些列表中。它更快。

标签: python pandas pandas-groupby


【解决方案1】:

您可以通过转换数据框来使用 numpy 访问单元格值 到一个 numpy 数组。此方法比 .loc 方法快。 但是,您确实需要知道列的位置。在下面 示例我想要与 A 中的 2 对应的 B 列中的值 柱子。

df = pd.DataFrame( {'A':[1,2,3],'B':[4,5,6],'C':[7,8,9]} )

# Make sure our A and B are where we think they are (optional)
A = df.columns.get_loc('A')
B = df.columns.get_loc('B')

# Convert to numpy array
df = df.values

# Get the value
B_val = df[:,B][ df[:,A] == 2 ][0]  

# Convert back to dataframe (optional)
df = pd.DataFrame(df, columns = ['A','B','C'])

#B_val = 5

您还可以将数据框转换为字典并以这种方式访问​​值。这比 .at[] 方法稍快,比 .loc[] 方法快很多。

df = pd.DataFrame( {'A':[1,2,3],'B':[4,5,6],'C':[7,8,9]} )

# Convert to dictionary
df = df.set_index('A').T.to_dict('list')
num = 2
B_val = df[num][0]

【讨论】:

    【解决方案2】:
    records.groupby(['user','document']).size().unstack('document').fillna(0)
    

    【讨论】:

      【解决方案3】:

      我们试试吧:

      df.set_index('user')['document'].str.get_dummies().sum(level=0)
      

      输出:

            article  blogpost  book
      user                         
      john        0         0     2
      jane        1         3     2
      jim         1         1     0
      

      【讨论】:

        【解决方案4】:

        有很多方法可以在没有循环的情况下实现这一点,pivotpivot_tablecrosstabgroupby count

        pd.crosstab(df.user,df.document)
        Out[1283]: 
        document  article  blogpost  book
        user                             
        jane            1         3     2
        jim             1         1     0
        john            0         0     2
        

        【讨论】:

        • 呜呜呜。从来不知道crosstab。非常方便。
        • @Wen 很棒的提示。谢谢!
        • @Nivi Yw~ :-) 干杯
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 1970-01-01
        • 1970-01-01
        • 2018-11-03
        • 1970-01-01
        相关资源
        最近更新 更多