pandas.DataFrameの各行内にある辞書を合計する効率的な方法

How to find sum of dictionaries in a pandas DataFrame across all rows? – StackOverflow

回答の方法は見た目エレガントだけど,効率はそんなに良くない(というかかなり悪い).
ちょっとN数を大きくすれば分かるが,defaultdict(int)を用いた方が20倍位速い.

import pandas as pd
from collections import defaultdict




df = pd.DataFrame({'keywords': [{'a': 3, 'b': 4, 'c': 5}, {'c':1, 'd':2}, {'a':5, 'c':21, 'd':4}, {'b':2, 'c':1, 'g':1, 'h':1, 'i':1}]})
print(df)

dd = defaultdict(int)
for d in df['keywords'].values.tolist():
  for k, v in d.items():
    dd[k] += v
res = pd.DataFrame.from_dict(dd, orient='index')
print(res)
                                   keywords
0                  {'a': 3, 'b': 4, 'c': 5}
1                          {'c': 1, 'd': 2}
2                 {'a': 5, 'c': 21, 'd': 4}
3  {'b': 2, 'c': 1, 'g': 1, 'h': 1, 'i': 1}
    0
a   8
b   6
c  28
d   6
g   1
h   1
i   1
df = pd.concat([df]*10000, ignore_index=True)
%%timeit
sum([Counter(x) for x in df['keywords'].values.tolist()], Counter())
res = pd.DataFrame.from_dict(dd, orient='index')
1 loop, best of 3: 306 ms per loop
%%timeit
dd = defaultdict(int)
for d in df['keywords'].values.tolist():
  for k, v in d.items():
    dd[k] += v
res = pd.DataFrame.from_dict(dd, orient='index')
100 loops, best of 3: 18.7 ms per loop

他の方法としては,pandas.io.json.json_normalizeを用いる方法がある.

from pandas.io.json import json_normalize


json_normalize(df['keywords'].values.tolist()).sum(0)
a     8.0
b     6.0
c    28.0
d     6.0
g     1.0
h     1.0
i     1.0
dtype: float64
df = pd.concat([df]*10000, ignore_index=True)
%timeit json_normalize(df['keywords'].values.tolist()).sum(0)
10 loops, best of 3: 57.7 ms per loop

まあ,下記の様に,時間効率を考えれば,
defaultdict(int)を用いるのがリーズナブルチョイスかなと.

 

def using_dd(df):
  dd = defaultdict(int)
  for d in df['keywords'].values.tolist():
    for k, v in d.items():
      dd[k] += v
  return pd.DataFrame.from_dict(dd, orient='index')


def using_Counter(df):
  sum([Counter(x) for x in df['keywords'].values.tolist()], Counter())
  return pd.DataFrame.from_dict(dd, orient='index')


def using_json_normalize(df):
  return json_normalize(df['keywords'].values.tolist()).sum(0)


df = pd.DataFrame({'keywords': [{'a': 3, 'b': 4, 'c': 5}, {'c':1, 'd':2}, {'a':5, 'c':21, 'd':4}, {'b':2, 'c':1, 'g':1, 'h':1, 'i':1}]})
%timeit using_dd(df)
%timeit using_Counter(df)
%timeit using_json_normalize(df)
%timeit pd.DataFrame(d for d in df['keywords'].values.tolist()).sum(0)

df = pd.concat([df]*100, ignore_index=True)
%timeit using_dd(df)
%timeit using_Counter(df)
%timeit using_json_normalize(df)
%timeit pd.DataFrame(d for d in df['keywords'].values.tolist()).sum(0)

df = pd.concat([df]*100, ignore_index=True)
%timeit using_dd(df)
%timeit using_Counter(df)
%timeit using_json_normalize(df)
%timeit pd.DataFrame(d for d in df['keywords'].values.tolist()).sum(0)

df = pd.concat([df]*100, ignore_index=True)
%timeit using_dd(df)
%timeit using_json_normalize(df)
%timeit pd.DataFrame(d for d in df['keywords'].values.tolist()).sum(0)
10000 loops, best of 3: 193 µs per loop
1000 loops, best of 3: 251 µs per loop
1000 loops, best of 3: 746 µs per loop
1000 loops, best of 3: 749 µs per loop

1000 loops, best of 3: 402 µs per loop
100 loops, best of 3: 3.16 ms per loop
1000 loops, best of 3: 1.31 ms per loop
1000 loops, best of 3: 1.33 ms per loop

100 loops, best of 3: 18.6 ms per loop
1 loop, best of 3: 307 ms per loop
10 loops, best of 3: 114 ms per loop
10 loops, best of 3: 111 ms per loop

1 loop, best of 3: 1.86 s per loop
1 loop, best of 3: 5.77 s per loop
1 loop, best of 3: 6.02 s per loop
カテゴリー: 未分類 パーマリンク

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト /  変更 )

Google フォト

Google アカウントを使ってコメントしています。 ログアウト /  変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト /  変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト /  変更 )

%s と連携中

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください