2016-11-25 94 views
1

我有一個熊貓數據框。我想通過使用一個列組合並對另一個列組合的不同值進行計數來對它進行分組。如何在按熊貓進行分組的同時對列組合中的不同值進行計數?

例如我有以下的數據幀:

a b c  d  e 
0 1 10 100 1000 10000 
1 1 10 100 1000 20000 
2 1 20 100 1000 20000 
3 1 20 100 2000 20000 

我可以按列ab和組它在列d計數不同值:

df.groupby(['a','b'])['d'].nunique().reset_index() 

結果我得到:

a b d 
0 1 10 1 
1 1 20 2 

但是,我想在列的組合中計算不同的值。例如,如果我使用cd,那麼在第一組中我只有一個唯一組合((100, 1000)),而在第二組中我有兩個不同的組合:(100, 1000)(100, 2000)

以下天真「泛化」不工作:

df.groupby(['a','b'])[['c','d']].nunique().reset_index() 

因爲nunique()並不適用於數據幀。

+1

難道你不能簡單地創建一個'f'列作爲'c'和'd'的組合嗎? –

+0

@NilsGudat,是的我雖然關於這種方法,但我不知道這是否是一個正確的方式去。我有以下擔憂。我在這兩列都有數字值,我應該如何從它們中構造出唯一的值?我可以將它們轉換爲字符串,然後連接字符串,但可能太慢。 – Roman

+0

沒有答案適合你? ;) – IanS

回答

5

您可以創建值轉換爲string到新列e的組合,然後使用SeriesGroupBy.nunique

df['e'] = df.c.astype(str) + df.d.astype(str) 
df = df.groupby(['a','b'])['e'].nunique().reset_index() 
print (df) 
    a b e 
0 1 10 1 
1 1 20 2 

您還可以使用Series,而無需創建新列:

df =(df.c.astype(str)+df.d.astype(str)).groupby([df.a, df.b]).nunique().reset_index(name='f') 
print (df) 
    a b f 
0 1 10 1 
1 1 20 2 

另一種更多鈔票的解決方案是創建元組:

df=(df[['c','d']].apply(tuple, axis=1)).groupby([df.a, df.b]).nunique().reset_index(name='f') 
print (df) 
    a b f 
0 1 10 1 
1 1 20 2 

answer另一個numpy的解決方案:

def f(x): 
    a = x.values 
    c = len(np.unique(np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1]))), return_counts=True)[1]) 
    return c 

print (df.groupby(['a','b'])[['c','d']].apply(f)) 

時序

#[1000000 rows x 5 columns] 
np.random.seed(123) 
N = 1000000 
df = pd.DataFrame(np.random.randint(30, size=(N,5))) 
df.columns = list('abcde') 
print (df) 

In [354]: %timeit (df.groupby(['a','b'])[['c','d']].apply(lambda g: len(g) - g.duplicated().sum())) 
1 loop, best of 3: 663 ms per loop 

In [355]: %timeit (df.groupby(['a','b'])[['c','d']].apply(f)) 
1 loop, best of 3: 387 ms per loop 

In [356]: %timeit (df.groupby(['a', 'b', 'c', 'd']).size().groupby(level=['a', 'b']).size()) 
1 loop, best of 3: 441 ms per loop 

In [357]: %timeit ((df.c.astype(str)+df.d.astype(str)).groupby([df.a, df.b]).nunique()) 
1 loop, best of 3: 4.95 s per loop 

In [358]: %timeit ((df[['c','d']].apply(tuple, axis=1)).groupby([df.a, df.b]).nunique()) 
1 loop, best of 3: 17.6 s per loop 
3

如果你不想來連接列,您可以應用計數的非重複數的函數:

df.groupby(['a','b'])[['c','d']].apply(lambda g: len(g) - g.duplicated().sum()) 
2

不要停在'a', 'b'的GROUPBY,包括你的一切正在看。

df.groupby(['a', 'b', 'c', 'd']).size() 

a b c d 
1 10 100 1000 2 
    20 100 1000 1 
      2000 1 
dtype: int64 

unstack得到了不同的看法

df.groupby(['a', 'b', 'c', 'd']).size().unstack(fill_value=0) 

enter image description here


真正得到你正在尋找

信息210

說:
組內a is 1; b is 10c and d的1個獨特組合。
a is 1; b is 20組內有c and d的2個獨特組合。

1

你可以擴展你的原始概念...

df.groupby(['a', 'b', 'c']).d.nunique() 

a b c 
1 10 100 1 
    20 100 2 
Name: d, dtype: int64 

可以刪除該指數c,專注於你想要的信息。

df.groupby(['a', 'b', 'c']).d.nunique().reset_index('c', drop=True) 

a b 
1 10 1 
    20 2 
Name: d, dtype: int64 
+0

我想在你的例子中,你只是在一列(「d」)中計算唯一值,我希望能夠計算來自多個列的值的唯一組合。所以,我最初的例子更一般。 – Roman

+0

就是這樣。使用包含您的列的一組,並在另一列上計算'nunique',就完全可以做到這一點。 – 2016-11-25 14:30:58

+0

如果對於'a,b,c = 1,10,100',我有2個唯一的d值,對'a,b,c = 1,10,200'我有3個唯一的d值。現在我應該再次用'a和b'分組,總結2和3,以發現對於'a,b = 1,10',我有5個獨特的c和d組合。 – Roman