データフレーム各行の連続したゼロをカウント

Count consecutive zeros over pandas rows – StackOverflow

これは,Pandas側で処理するベネフィットは殆どなくて,数値演算なのでnumpy vectorizedなnumpyソリューションが考えられる.最も単純には,関連同様に処理できて,そうするだけでも,Divakarさん以外の回答と比べた時に,5倍 – 9倍高速化する事ができる.

import pandas as pd
import numpy as np


def seq_zerocount_1d(a):
    arr = (a==0).astype(int)
    ind = np.concatenate(([0], np.flatnonzero(np.diff(arr))+1))
    arr[ind[1:]] -= np.add.reduceat(arr, ind)[:-1]
    return arr.cumsum(0)


def split_zerocount(a):
    res = np.column_stack((np.apply_along_axis(seq_zerocount_1d, 1, a), np.zeros((len(a), 1), dtype=int)))
    cond1 = res != 0
    cond2 = cond1[:, 1:] != cond1[:, :-1]
    cond = np.logical_and(cond1[:, :-1], cond2)
    r, c = cond.nonzero()
    return pd.Series(np.split(res[r, c], np.bincount(r)[:-1].cumsum()))


df = pd.DataFrame({'2010':[0, 45, 5], '2011': [12, 56, 0], '2012': [11, 22, 0], '2013': [0, 5, 0], '2014': [0, 0, 0]})
print(df)
print(split_zerocount(df.values))
   2010  2011  2012  2013  2014
0     0    12    11     0     0
1    45    56    22     5     0
2     5     0     0     0     0
0    [1, 2]
1       [1]
2       [4]
dtype: object
from itertools import groupby


def islandlen_perrow(df, trigger_val=0):
    a=df.values==trigger_val
    pad = np.zeros((a.shape[0],1),dtype=bool)
    mask = np.hstack((pad, a, pad))
    mask_step = mask[:,1:] != mask[:,:-1]
    idx = np.flatnonzero(mask_step)
    island_lens = idx[1::2] - idx[::2]
    n_islands_perrow = mask_step.sum(1)//2
    out = np.split(island_lens,n_islands_perrow[:-1].cumsum())
    return out


def count_zeros(x):
    return [sum(1 for _ in group) for key, group in groupby(x, key=lambda i: i == 0) if key]


df = pd.DataFrame(np.random.randint(0, 4, (1000, 1000)))
%timeit split_zerocount(df.values)
%timeit pd.Series(islandlen_perrow(df, trigger_val=0))
%timeit df.apply(count_zeros, axis=1)
%timeit v = df.stack();m = v.eq(0);m.ne(m.shift()).cumsum().where(m).dropna().groupby(level=0).value_counts(sort=False).groupby(level=0).apply(list)
%timeit [[len(list(grp)) for flag, grp in groupby(row, key=bool) if not flag] for row in df.values]

df = pd.DataFrame(np.random.randint(0, 100, (1000, 1000)))
%timeit split_zerocount(df.values)
%timeit pd.Series(islandlen_perrow(df, trigger_val=0))
%timeit df.apply(count_zeros, axis=1)
%timeit v = df.stack();m = v.eq(0);m.ne(m.shift()).cumsum().where(m).dropna().groupby(level=0).value_counts(sort=False).groupby(level=0).apply(list)
%timeit [[len(list(grp)) for flag, grp in groupby(row, key=bool) if not flag] for row in df.values]

df = pd.DataFrame(np.random.randint(0, 100, (1000, 10000)))
%timeit split_zerocount(df.values)
%timeit pd.Series(islandlen_perrow(df, trigger_val=0))
%timeit df.apply(count_zeros, axis=1)
%timeit v = df.stack();m = v.eq(0);m.ne(m.shift()).cumsum().where(m).dropna().groupby(level=0).value_counts(sort=False).groupby(level=0).apply(list)
%timeit [[len(list(grp)) for flag, grp in groupby(row, key=bool) if not flag] for row in df.values]

df = pd.DataFrame(np.random.randint(0, 100, (10000, 1000)))
%timeit split_zerocount(df.values)
%timeit pd.Series(islandlen_perrow(df, trigger_val=0))
%timeit df.apply(count_zeros, axis=1)
%timeit v = df.stack();m = v.eq(0);m.ne(m.shift()).cumsum().where(m).dropna().groupby(level=0).value_counts(sort=False).groupby(level=0).apply(list)
%timeit [[len(list(grp)) for flag, grp in groupby(row, key=bool) if not flag] for row in df.values]
10 loops, best of 3: 61.8 ms per loop
100 loops, best of 3: 10.8 ms per loop
1 loop, best of 3: 314 ms per loop
1 loop, best of 3: 344 ms per loop
1 loop, best of 3: 241 ms per loop

10 loops, best of 3: 44.1 ms per loop
100 loops, best of 3: 6.39 ms per loop
10 loops, best of 3: 179 ms per loop
1 loop, best of 3: 213 ms per loop
10 loops, best of 3: 118 ms per loop

1 loop, best of 3: 241 ms per loop
10 loops, best of 3: 45.1 ms per loop
1 loop, best of 3: 1.55 s per loop
1 loop, best of 3: 1.17 s per loop
1 loop, best of 3: 1.16 s per loop

1 loop, best of 3: 441 ms per loop
10 loops, best of 3: 61.9 ms per loop
1 loop, best of 3: 1.76 s per loop
1 loop, best of 3: 2.06 s per loop
1 loop, best of 3: 1.18 s per loop

 
 
関連:
pandas.DataFrame.applyは(時空間効率の観点からは)使用するべきではない – 使用すべき明確な理由がない限りpandas.DataFrame.applyはリーズナブルチョイスになり得ない

特定の値のギャップを数える – 任意の要素間のカウント

シーケンシャルな値の累積和(cumsum)

ある配列から別の1次元numpyアレイの要素の出現数を調べる – 配列aについて配列bの要素をカウント

2D Numpy配列で最初の要素が重複している行の平均 – 2Dなnumpy.ndarrayの最初の要素(1列目)で各行をグルーピングして集計(平均を算出)

一貫性のある最大領域(最大ブロブ)を取得

Counting Sort(Python/Numpy)

カテゴリー: 未分類 パーマリンク

データフレーム各行の連続したゼロをカウント への2件のフィードバック

  1. ピンバック: pandasデータフレーム列内のNaNに基づいて累積和をリセット – 任意の条件に基づいた範囲で累積和を求める | 粉末@それは風のように (日記)

  2. ピンバック: データフレーム列内の連続した値の和(任意長の範囲で合計) | 粉末@それは風のように (日記)

コメントを残す

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

WordPress.com ロゴ

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

Google フォト

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

Twitter 画像

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

Facebook の写真

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

%s と連携中

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