ECMAScript 6 имеет «генераторы», которые позволяют вам легко программировать в асинхронном стиле.
function* myGenerator() {
const callback = yield;
let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
console.log("response is:", response);
// examples of other things you can do
yield setTimeout(callback, 1000);
console.log("it delayed for 1000ms");
while (response.statusText === "error") {
[response] = yield* anotherGenerator();
}
}
Для запуска вышеуказанного кода вы делаете это:
const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function
Если вам нужно настроить таргетинг на браузеры, которые не поддерживают ES6, вы можете запустить код через Babel или short-compiler для генерации ECMAScript 5.
Обратный вызов ...args
завернут в массив и разрушен, когда вы их читаете так что шаблон может справиться с обратными вызовами, которые имеют несколько аргументов. Например, с узлом fs :
const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
Для первой части вы можете передать имя столбца для ключей и список функций для значений:
In [28]: df
Out[28]:
A B C D E GRP
0 0.395670 0.219560 0.600644 0.613445 0.242893 0
1 0.323911 0.464584 0.107215 0.204072 0.927325 0
2 0.321358 0.076037 0.166946 0.439661 0.914612 1
3 0.133466 0.447946 0.014815 0.130781 0.268290 1
In [26]: f = {'A':['sum','mean'], 'B':['prod']}
In [27]: df.groupby('GRP').agg(f)
Out[27]:
A B
sum mean prod
GRP
0 0.719580 0.359790 0.102004
1 0.454824 0.227412 0.034060
UPDATE 1:
Поскольку агрегатная функция работает над Series, ссылки на другие имена столбцов теряются. Чтобы обойти это, вы можете ссылаться на полный блок данных и индексировать его с помощью индексов группы в лямбда-функции.
Вот хакерский обход:
In [67]: f = {'A':['sum','mean'], 'B':['prod'], 'D': lambda g: df.loc[g.index].E.sum()}
In [69]: df.groupby('GRP').agg(f)
Out[69]:
A B D
sum mean prod <lambda>
GRP
0 0.719580 0.359790 0.102004 1.170219
1 0.454824 0.227412 0.034060 1.182901
Здесь результирующий ' D 'состоит из суммированных значений «E».
UPDATE 2:
Вот такой метод, который, я думаю, сделает все, что вы просите. Сначала создайте пользовательскую лямбда-функцию. Ниже, g ссылается на группу. При агрегировании g будет Серией. Передача g.index
в df.ix[]
позволяет выбрать текущую группу из df. Затем я тестирую, если столбец C меньше 0,5. Возвращенная логическая серия передается в g[]
, которая выбирает только те строки, которые соответствуют критериям.
In [95]: cust = lambda g: g[df.loc[g.index]['C'] < 0.5].sum()
In [96]: f = {'A':['sum','mean'], 'B':['prod'], 'D': {'my name': cust}}
In [97]: df.groupby('GRP').agg(f)
Out[97]:
A B D
sum mean prod my name
GRP
0 0.719580 0.359790 0.102004 0.204072
1 0.454824 0.227412 0.034060 0.570441
Ответ Теда потрясающий. Я закончил тем, что использовал меньшую версию, если кто-то заинтересован. Полезно, если вы ищете одно агрегирование, которое зависит от значений из нескольких столбцов:
df=pd.DataFrame({'a': [1,2,3,4,5,6], 'b': [1,1,0,1,1,0], 'c': ['x','x','y','y','z','z']})
a b c
0 1 1 x
1 2 1 x
2 3 0 y
3 4 1 y
4 5 1 z
5 6 0 z
df.groupby('c').apply(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())
c
x 2.0
y 4.0
z 5.0
Мне нравится этот подход, поскольку я все еще могу использовать агрегат. Возможно, люди скажут мне, почему применение необходимо для получения нескольких столбцов при выполнении группировок по группам.
Теперь это кажется очевидным, но пока вы не выбираете интересующий столбец напрямую после groupby у вас будет доступ ко всем столбцам фреймворка из вашей функции агрегации.
df.groupby('c')['a'].aggregate(lambda x: x[x>1].mean())
df.groupby('c').aggregate(lambda x: x[(x['a']>1) & (x['b']==1)].mean())['a']
df.groupby('c').aggregate(lambda x: x['a'][(x['a']>1) & (x['b']==1)].mean())
Надеюсь, это поможет.
Вторая половина принятого в настоящее время ответа устарела и имеет два отклонения. Прежде всего, вы больше не можете передавать словарь словарей в метод agg
groupby. Во-вторых, никогда не используйте .ix
.
Если вы хотите работать с двумя отдельными столбцами одновременно, я бы предложил использовать метод apply
, который implicity передает DataFrame к прикладной функции. Давайте используем аналогичный блок данных, как тот, что приведен выше
df = pd.DataFrame(np.random.rand(4,4), columns=list('abcd'))
df['group'] = [0, 0, 1, 1]
df
a b c d group
0 0.418500 0.030955 0.874869 0.145641 0
1 0.446069 0.901153 0.095052 0.487040 0
2 0.843026 0.936169 0.926090 0.041722 1
3 0.635846 0.439175 0.828787 0.714123 1
Словарь, сопоставленный с именами столбцов, для функций агрегации, по-прежнему отлично подходит для выполнения агрегации.
df.groupby('group').agg({'a':['sum', 'max'],
'b':'mean',
'c':'sum',
'd': lambda x: x.max() - x.min()})
a b c d
sum max mean sum <lambda>
group
0 0.560541 0.507058 0.418546 1.707651 0.129667
1 0.187757 0.157958 0.887315 0.533531 0.652427
Если вам не нравится это уродливое имя столбца лямбда, вы можете использовать обычную функцию и указать специальное имя для специального атрибута __name__
следующим образом:
def max_min(x):
return x.max() - x.min()
max_min.__name__ = 'Max minus Min'
df.groupby('group').agg({'a':['sum', 'max'],
'b':'mean',
'c':'sum',
'd': max_min})
a b c d
sum max mean sum Max minus Min
group
0 0.560541 0.507058 0.418546 1.707651 0.129667
1 0.187757 0.157958 0.887315 0.533531 0.652427
apply
и возвращая серию Теперь, если у вас было несколько столбцов, которые должны были взаимодействовать друг с другом, вы не можете использовать agg
, который неявно передает Серию в агрегирующую функцию. При использовании apply
вся группа в качестве DataFrame передается в функцию.
Я рекомендую сделать одну настраиваемую функцию, которая возвращает Серию всех агрегатов. Используйте индекс Series в качестве меток для новых столбцов:
def f(x):
d = {}
d['a_sum'] = x['a'].sum()
d['a_max'] = x['a'].max()
d['b_mean'] = x['b'].mean()
d['c_d_prodsum'] = (x['c'] * x['d']).sum()
return pd.Series(d, index=['a_sum', 'a_max', 'b_mean', 'c_d_prodsum'])
df.groupby('group').apply(f)
a_sum a_max b_mean c_d_prodsum
group
0 0.560541 0.507058 0.418546 0.118106
1 0.187757 0.157958 0.887315 0.276808
Если вы любите MultiIndexes, вы все равно можете вернуть серию с такими же:
def f_mi(x):
d = []
d.append(x['a'].sum())
d.append(x['a'].max())
d.append(x['b'].mean())
d.append((x['c'] * x['d']).sum())
return pd.Series(d, index=[['a', 'a', 'b', 'c_d'],
['sum', 'max', 'mean', 'prodsum']])
df.groupby('group').apply(f_mi)
a b c_d
sum max mean prodsum
group
0 0.560541 0.507058 0.418546 0.118106
1 0.187757 0.157958 0.887315 0.276808