2017-01-24 3 views
8

Я хотел бы выделить бункеры для «S» отдельно, где каждый столбец (X & Y)> 0,5 или несколько ящиков> 0,5 * 'количество строк.Выбор строк в мультииндексированном фрейме данных

В примере;

для 'AR1' должен только быть выбран бен 4, так как 'X' и 'Y' является> 0,5 (синего указано)

для 'PO1' должны бункера 1, 2, 3 и 4 можно выбрать, потому что 'X' и 'Y'> (4 * 0,5) (желтый обозначен).

Я пробовал это раньше с for loop, но это не сработало правильно; 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

Что это должно выглядеть это ФР, как показано в этом вопросе, но строки, которые не квалифицируют отфильтрованы. Я проверяю это; значения в X и Y> 0,5 для любой строки (bin) отдельно или в сочетании. Комбинации строк только последовательно, 2, 3, 4 или 5 строк вместе.

I.e, тогда комбинации строк для 0; 0 + 1, 0 + 1 + 2, 0 + 1 + 2 + 3 и 0 + 1 + 2 + 3 + 4. Для 1; 1 + 2, 1 + 2 + 3 и 1 + 2 + 3 + 4 и т. Д.

Несколько строк будут суммироваться с количеством строк x 0,5, X и Y должны быть> 2,5 для строк от 0 до 4 для пример.

EDIT2: @JohnE и piRSquared, обе ваши решения работают, которые, однако, будут работать лучше, если в кадре данных есть другие столбцы, которые не должны оцениваться?

Кроме того, что, если я хочу добавить дополнительное условие в ваши решения?

EDIT3: @piRSquared, Когда подмножество некоторых столбцов я получаю только те, которые мне нужны, и мне нужны все они, а не только подмножество.

Не могли бы вы помочь? Благодарю.

ответ

3

Это Векторизованный подход только с одной петлей на верхнем уровне (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 
     ) 

Объяснение

стратегии

  • numpy.triu_indices: Мне нужно оценить каждое возможное окно для прокатки mean больше, чем некоторые threshold. Я собираюсь захватить каждое возможное окно, начиная с позиции 0 до 0, затем от 0 до 1, затем ... затем от 1 до 1, от 1 до 2 ... так далее и т. Д.Но я должен всегда начинать работу до того, как закончу. Я могу получить доступ к этим комбинациям с помощью numpy.triu_indices.
  • cumsum: Было бы довольно сложно (выполнимо) получить расширенные массивы, указанные каждой комбинацией индексов, которые я получаю от np.triu_indices. Лучшим способом является вычисление cumsum и переход от одного индекса к другому.
  • Мне нужно добавить нули в мой cumsum, чтобы я мог изменить значение для первой строки.
  • Но суммы не являются средствами. Мне нужно разделить на количество строк, чтобы получить средства. Удобно, что разница между конечными и начальными позициями - это точно количество строк и, следовательно, соответствующее число, чтобы разделить суммы, чтобы вычислить средства.
  • Теперь, когда у меня есть средства, e/d, я проверяю, какие > threshold и определите, какие комбинации начального и конечного положения имеют значения, превышающие пороговое значение для обоих столбцов.
  • Затем я идентифицирую комбинацию с наибольшим количеством строк среди тех, у которых есть средства, превышающие пороговое значение.
  • разматываю позиции и восстановить dataframe
  • groupby и apply ... QED

испытание временем

enter image description here


с большим количеством данных

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) 

Разница во времени еще более драматичным

enter image description here

+0

Спасибо, это работает. Я внес изменения. Мне нужно использовать ваше решение или JohnE на моем реальном data_set. Однако у этого набора есть несколько дополнительных препятствий для меня. – Zanshin

+1

Ницца! Я уничтожил мой, поскольку я знал, что это было довольно медленно, и в любом случае вы включили мой код в тайминги, так что, если кто-то заботится об этом. У меня было ощущение, что делать это будет нелегко, так что я странно рад видеть, что это было нелегко! – JohnE

+0

Я могу оценить настроение ;-) – piRSquared