2016-04-08 2 views
1

Я пытаюсь использовать Pandas для изменения огромного набора данных с результатами операции groupby. Мне нужно найти минимальное значение групп данных при игнорировании нулей, но вернуть этот же ноль, если это единственное значение в группе.Pandas изменить колонку с результатами группы при игнорировании некоторых значений

Рассмотрим фиктивную данные:

>>> a=pd.DataFrame(dict(item_id=[1,1,1,2,2,2], pos_id=[3,7,7,7,7,7], target='T1 T2 T3 T1 T2 T3'.split(), val=[8,0,0,41,0,55])) 
>>> a 
    item_id pos_id target val 
0  1  3  T1 8 
1  1  7  T2 0 
2  1  7  T3 0 
3  2  7  T1 41 
4  2  7  T2 0 
5  2  7  T3 55 

Для каждого item_id в pos_id == 7 и цели == «T2», я хотел бы получить наименьшее ненулевое Вэл всей группы (без учета целевых), и замените его на месте с этим!

Итак, я хотел бы получить это в конце концов:

item_id pos_id target val 
0  1  3  T1 8 <-- this row has the wrong pos_id and is ignored 
1  1  7  T2 0 <-- this one maintains zero (all group has zeros) 
2  1  7  T3 0 
3  2  7  T1 41 
4  2  7  T2 41 <-- this one gets the smallest of item_id group 
5  2  7  T3 55 

Я попытался это:

>>> a.ix[(a.pos_id == 7) & (a.target == 'T2'), 'val'] = a.ix[a.pos_id == 7].groupby('item_id').val.min().values 

Но это не работает, потому что нули не игнорируются. И я не могу просто игнорировать их, потому что серия по обе стороны задания будет иметь разные размеры!

>>> a.ix[(a.pos_id == 7) & (a.target == 'T2'), 'val'].size, a.ix[(a.pos_id == 7) & (a.val != 0)].groupby('item_id').val.min().values.size 
(2, 1) 

Я также попытался поставить высокое значение в этих нулей, так что первая будет почти успешно:

>>> a.ix[(a.pos_id == 7) & (a.val == 0), 'val'] = 9999 
>>> a 
    item_id pos_id target val 
0  1  3  T1  8 
1  1  7  T2 9999 
2  1  7  T3 9999 
3  2  7  T1 41 
4  2  7  T2 9999 
5  2  7  T3 55 

Но:

>>> a.ix[(a.pos_id == 7) & (a.target == 'T2'), 'val'] = a.ix[a.pos_id == 7].groupby('item_id').val.min().values 
>>> a 
    item_id pos_id target val 
0  1  3  T1  8 
1  1  7  T2 9999 <-- this one should maintain zero... 
2  1  7  T3 9999 
3  2  7  T1 41 
4  2  7  T2 41 <-- this one works! 
5  2  7  T3 55 

Но теперь я должен был бы возвратите к нулю значения, которые не должны быть изменены ... Ох, это должен быть лучший способ!

Как мне установить это значение с помощью groupby, игнорируя некоторые значения, на одном более простом шаге? И, пожалуйста, также считайте, что производительность важна, поскольку набор данных имеет несколько гигабайт ...

СПАСИБО!

ответ

1

Я думаю, вы можете использовать условие с numpy.where для проверки, если all значения 0, то выход 0 остальное min без 0 значения с transform вместо apply:

a=pd.DataFrame(dict(item_id=[1,1,1,2,2,2], 
        pos_id=[3,7,7,7,7,7], 
        target='T1 T2 T3 T1 T2 T3'.split(), 
        val=[8,0,0,41,0,55])) 
print a 
    item_id pos_id target val 
0  1  3  T1 8 
1  1  7  T2 0 
2  1  7  T3 0 
3  2  7  T1 41 
4  2  7  T2 0 
5  2  7  T3 55 

a.ix[(a.pos_id == 7) & (a.target == 'T2'), 'val'] = 
a.ix[a.pos_id == 7].groupby('item_id').val.transform(lambda x: np.where((x == 0).all(), 
                     0, 
                     x[x!=0].min())) 
print a 
    item_id pos_id target val 
0  1  3  T1 8 
1  1  7  T2 0 
2  1  7  T3 0 
3  2  7  T1 41 
4  2  7  T2 41 
5  2  7  T3 55 
+0

Спасибо вам jezrael! Таким образом, я не был так далек от ответа, но я не знаю, что есть numpy ... Во всяком случае, у меня есть еще одна проблема: действительно ли безопасно назначать значения так, как я делал, с параметрами '.values' индексы полосы? Гарантировано ли, что обе серии всегда будут в одном порядке? Нужно ли мне устанавливать порядок в Серии, чтобы убедиться в этом? Если да, пожалуйста, покажите мне лучший способ гарантировать это ... Еще раз спасибо! – rsalmei

+0

Хммм, твердый вопрос. Но я нашел решение без '.values', пожалуйста, проверьте его. – jezrael

+0

Это потрясающе, спасибо @jezrael! – rsalmei

Смежные вопросы