2016-06-26 17 views
2

einen Datenrahmen in Python zu haben:Pandas - zählen, wenn mehrere Bedingungen

CASE TYPE 
1   A 
1   A 
1   A 
2   A 
2   B 
3   B 
3   B 
3   B 

wie kann ich ein Ergebnis Datenrahmen schaffen, die alle Fälle ergeben würden und entweder ein „A“, wenn nur der Fall, „A sind“ hat zugewiesen, "B" wenn es nur "B's" oder "MIXED" wäre, wenn der Fall sowohl A als auch B hätte?

würde dann sein:

Case  Type 
1  A 
2  MIXED 
3  B 
+0

Was haben Sie müde ? – Merlin

Antwort

2

Hier ist eine Option, wo wir zunächst die TYPE als Liste Gruppe von CASE sammeln und dann überprüfen Sie die length einzigartigen TYPE, wenn er größer als 1 ist, Rückkehr MIXED sonst das TYPE von selbst:

import pandas as pd 
import numpy as np 
groups = df.groupby('CASE').agg(lambda g: [g.TYPE.unique()]). 
      apply(lambda row: np.where(len(row.TYPE) > 1, 'MIXED', row.TYPE[0]), axis = 1) 
groups 

# CASE 
# 1   A 
# 2  MIXED 
# 3   B 
# dtype: object 
1

hier ist ein bisschen hässlich, aber nicht, dass eine langsame Lösung:

In [154]: df 
Out[154]: 
    CASE TYPE 
0  1 A 
1  1 A 
2  1 A 
3  2 A 
4  2 B 
5  3 B 
6  3 B 
7  3 B 
8  4 C 
9  4 C 
10  4 B 

In [155]: %paste 
(df.groupby('CASE')['TYPE'] 
    .apply(lambda x: x.head(1) if x.nunique() == 1 else pd.Series(['MIX'])) 
    .reset_index() 
    .drop('level_1', 1) 
) 
## -- End pasted text -- 
Out[155]: 
    CASE TYPE 
0  1 A 
1  2 MIX 
2  3 B 
3  4 MIX 

Timing: gegen 800K Reihen DF:

In [191]: df = pd.concat([df] * 10**5, ignore_index=True) 

In [192]: df.shape 
Out[192]: (800000, 3) 

In [193]: %timeit Psidom(df) 
1 loop, best of 3: 235 ms per loop 

In [194]: %timeit capitalistpug(df) 
1 loop, best of 3: 419 ms per loop 

In [195]: %timeit Alberto_Garcia_Raboso(df) 
10 loops, best of 3: 112 ms per loop 

In [196]: %timeit MaxU(df) 
10 loops, best of 3: 80.4 ms per loop 
2
df['NTYPES'] = df.groupby('CASE').transform(lambda x: x.nunique()) 
df.loc[df.NTYPES > 1, 'TYPE'] = 'MIXED' 
df.groupby('TYPE', as_index=False).first().drop('NTYPES', 1) 

    TYPE CASE 
0  A  1 
1  B  3 
2 MIXED  2 
1

Hier ist eine (zugegebenermaßen over-engineered) Lösung, die langsam über Gruppen und DataFrame.apply (diese sind vermeidet Looping, so vermeidet sie werden kann wichtig, wenn Ihr Datensatz ausreichend groß ist).

import pandas as pd 
df = pd.DataFrame({'CASE': [1]*3 + [2]*2 + [3]*3, 
        'TYPE': ['A']*4 + ['B']*4}) 

Wir Gruppe von CASE und berechnen die relativen Häufigkeiten von TYPEA oder B sein:

grouped = df.groupby('CASE') 
vc = (grouped['TYPE'].value_counts(normalize=True) 
        .unstack(level=0) 
        .fillna(0)) 

Hier ist, was vc sieht aus wie

CASE 1 2 3 
TYPE 
A  1.0 0.5 0.0 
B  0.0 0.5 0.0 

Beachten Sie, dass alle Informationen in enthalten ist die erste Reihe. Schneiden der Reihe in die Behälter mit pd.cut gibt das gewünschte Ergebnis:

tolerance = 1e-10 
bins = [-tolerance, tolerance, 1-tolerance, 1+tolerance] 
types = pd.cut(vc.loc['A'], bins=bins, labels=['B', 'MIXED', 'A']) 

Wir erhalten:

CASE 
1  A 
2 MIXED 
3  B 
Name: A, dtype: category 
Categories (3, object): [B < MIXED < A] 

Für eine gute Maßnahme können wir die types Serie umbenennen:

types.name = 'TYPE'