2017-01-24 18 views
8

Chciałbym wyodrębnić osobno pojemniki dla "S", gdzie każda kolumna (X & Y)> 0,5 lub wiele pojemników> 0,5 * "liczba rzędów".Wybieranie wierszy w wielowierszowej ramce danych

W przykładzie;

dla 'AR1' należy wybrać tylko pojemnik 4, ponieważ 'X' i 'Y' jest> 0,5 (niebieski wskazano)

dla 'PA1' należy pojemniki 1, 2, 3 i 4 jest zaznaczone, ponieważ "X" i "Y" są> (4 * 0,5) (kolor żółty wskazany).

Próbowałem już wcześniej z for loop, ale to nie działało poprawnie; Selecting multiple (neighboring) rows conditionally

np.random.seed(0) 

N = 20 
S = ['AR1', 'PO1'] 

df = pd.DataFrame(
    {'X':np.random.uniform(-1,1,N), 
    'Y':np.random.uniform(-1,1,N), 
    'S':np.random.choice(S,N), 
    }) 

df['bins_X'] = df.groupby('S')['X'].apply(pd.qcut, q=5, labels=np.arange(5)) # create bins per column 'S' 

def func(df):                 # create function to group per 'S' and their bins 
    df1 = df.groupby(['S','bins_X']).sum() 
    new_cols= list(zip(df1.columns.get_level_values(0))) 
    df1.columns = pd.MultiIndex.from_tuples(new_cols) 
    return df1 

print func(df) 

enter image description here

EDIT

Co powinno wyglądać to df jak pokazano w pytaniu, ale wiersze, które nie kwalifikują się odfiltrowane. Sprawdzam to; wartości w X i Y> 0,5 dla dowolnego wiersza (bin) oddzielnie lub łącznie. Kombinacje rzędów tylko kolejno, 2, 3, 4 lub 5 rzędów łącznie.

Np. Kombinacje wierszy dla 0 są wówczas; 0 + 1, 0 + 1 + 2, 0 + 1 + 2 + 3 i 0 + 1 + 2 + 3 + 4. Za 1 ; 1 + 2, 1 + 2 + 3 i 1 + 2 + 3 + 4 itd.

Wiele wierszy stanowiłoby sumę rzędów x 0,5, X i Y musiałyby być> 2,5 dla wierszy 0 do 4 dla przykład.

EDIT2: @JohnE i piRSquared, oba twoje rozwiązania działają, który jednak działałby lepiej, gdyby nie było innych kolumn w ramce danych, które nie powinny być oceniane?

Co jeśli chciałbym dodać dodatkowy warunek do twoich rozwiązań?

EDIT3: @piRSquared, Podczas podzbioru niektórych kolumn otrzymuję tylko te, które zostały mi zwrócone, gdzie potrzebuję ich wszystkich, nie tylko podsegmentowane.

Czy możesz pomóc? Dzięki.

Odpowiedz

3

to zwektoryzowany podejście tylko jednej pętli na najwyższym poziomie (groupby.apply)

# columns that I care about 
cols = ['X', 'Y'] 
df1.groupby(level=0)[cols].apply(find_window) 

enter image description here


def find_window(df): 
    v = df.values 
    s = np.vstack([np.zeros((1, v.shape[1])), v.cumsum(0)]) 

    threshold = .5 

    r, c = np.triu_indices(s.shape[0], 1) 
    d = (c - r)[:, None] 
    e = s[c] - s[r] 
    mask = (e/d > threshold).all(1) 
    rng = np.arange(mask.shape[0]) 

    if mask.any(): 
     idx = rng[mask][d[mask].argmax()] 

     i0, i1 = r[idx], c[idx] 
     return pd.DataFrame(
      v[i0:i1], 
      df.loc[df.name].index[i0:i1], 
      df.columns 
     ) 

Objaśnienie

strategia

  • numpy.triu_indices: muszę ocenić każdą możliwą okno do skręcania mean większej niż jakiś threshold. Zamierzam uchwycić każde możliwe okno zaczynając od pozycji 0 do 0, następnie od 0 do 1, następnie ... od 1 do 1, od 1 do 2 ... i tak dalej.Ale zawsze muszę zacząć od pozycji, zanim skończę. Mogę uzyskać dostęp do tych kombinacji za pomocą numpy.triu_indices.
  • cumsum: Byłoby trochę trudnym (wykonalne), aby uzyskać rozszerzone tablice określone przez każdą kombinację indeksów, które dostaję od np.triu_indices. Lepszym sposobem jest obliczenie wartości cumsum i odejście od jednego indeksu do drugiego.
  • Muszę uzupełniać zera do mojego cumsum, aby móc wziąć różnicę dla pierwszego wiersza.
  • Ale sumy nie są środkami. Muszę podzielić przez liczbę rzędów, aby uzyskać środki. Dogodnie różnica między położeniem końcowym i początkowym jest dokładnie liczbą rzędów, a zatem odpowiednią liczbą do podzielenia sum w celu obliczenia średnich.
  • Teraz, gdy mam środki, e/d, sprawdzam, które są > threshold i określić, które kombinacje pozycji początkowej i końcowej mają większe wartości niż próg dla obu kolumn.
  • Następnie identyfikuję kombinację z największą liczbą wierszy spośród tych, które mają wartość większą niż próg.
  • I odprężyć stanowisk i zrekonstruować dataframe
  • groupby i apply ... QED

Próba

enter image description here


więcej danych

np.random.seed(0) 

N = 300 
S = ['AR1', 'PO1', 'AR2', 'PO2', 'AR3', 'PO3'] 

df = pd.DataFrame(
    {'X':np.random.uniform(-1,1,N), 
    'Y':np.random.uniform(-1,1,N), 
    'S':np.random.choice(S,N), 
    }) 

df['bins_X'] = df.groupby('S')['X'].apply(pd.qcut, q=20, labels=np.arange(20)) # create bins per column 'S' 

def func(df):                 # create function to group per 'S' and their bins 
    df1 = df.groupby(['S','bins_X']).sum() 
    new_cols= list(zip(df1.columns.get_level_values(0))) 
    df1.columns = pd.MultiIndex.from_tuples(new_cols) 
    return df1 

df1 = func(df) 

Różnica czasu jest jeszcze bardziej dramatyczna

enter image description here

+0

Dzięki, to działa. Włączyłem edycję. Muszę użyć albo twojego rozwiązania, albo JohnE's na moim realnym zestawie danych. Ten zestaw ma jednak dla mnie kilka dodatkowych przeszkód. – Zanshin

+1

Nice! Wymazywałam moje, ponieważ wiedziałam, że jest dość powolna i mimo to włączyłeś mój kod w czasie, więc jeśli ktoś się tym przejmuje, to jest. Miałem wrażenie, że nie jest to łatwe do zrobienia w numpy, więc jestem niesamowicie szczęśliwy, widząc, że nie było to łatwe! – JohnE

+0

Mogę docenić sentyment ;-) – piRSquared