groupby, пользовательская функция использует столбец каждые первые 4 строки (после группировки) - PullRequest
0 голосов
/ 18 июня 2019

Допустим, у меня есть следующий фрейм данных.

import numpy as np
import pandas as pd

df = pd.DataFrame({'name':['a', 'a', 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c'],
                   'id':[0,1,2,3,4, 0, 1,2,3, 0, 1, 2], 
                   'val':[0.1, 0.2, 0.02, 0.52, 0.017,0.87, 0.24, 0.67, 0.9, 1.0, 0.99, 0.56]})

df

name    id  val
0   a   0   0.100
1   a   1   0.200
2   a   2   0.020
3   a   3   0.520
4   a   4   0.017
5   b   0   0.870
6   b   1   0.240
7   b   2   0.670
8   b   3   0.900
9   c   0   1.000
10  c   1   0.990
11  c   2   0.560

Теперь я хочу это сделать.

Я хочу сгруппировать имя и применить пользовательскую функцию к фрейму данных.

После группировки по имени я хочу проверить столбец id и, если он содержит как минимум 4 строки, затем применить функцию, которая вызывает другую (calc), которая получает в качестве входных данных массив numpy, который содержит4 первых идентификатора.

Например, я хочу применить функцию для name: a and b, поскольку они имеют id: 0,1,2,3,4 и 0,1,2,3Итак, у них обоих по крайней мере 4 строки.

И я хочу использовать первые 4 строки, чтобы использовать их в качестве входных данных для функции calc.

def calc(p):

    return p[0] + p[1] + p[2] + p[3]

Теперь что-тонапример, для пользовательской функции (она не работает):

def myfunc(data):
    if (data.id.values <=3):
        val1 = data[data.id==0].val.values
        val2 = data[data.id==1].val.values
        val3 = data[data.id==2].val.values
        val4 = data[data.id==3].val.values


    data['calc'] = calc(np.array([val1, val2, val3, val4]))
    return data

Это дает мне The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Я не могу найти способ правильно подсчитать id изаполните значения.

Некоторые уточнения

Относительно функции calc.Я хочу сделать более сложные вычисления (не сложение).Функция calc должна получить в качестве входных данных массив по крайней мере с 4 значениями.

Ожидаемый результат будет:

name    calc
a       0.84 (0.1+0.2+0.020+0.520)
b       2.68 (0.870+0.240+0.670+0.900)

or maybe something like (since it is name based):

name    id  val    calc
0   a   0   0.100  0.84
1   a   1   0.200  0.84
2   a   2   0.020  0.84
3   a   3   0.520  0.84
4   a   4   0.017  0.84
5   b   0   0.870  2.68
6   b   1   0.240  2.68
7   b   2   0.670  2.68
8   b   3   0.900  2.68
9   c   0   1.000  
10  c   1   0.990
11  c   2   0.560

Обновление

Iизменено (в соответствии с ответом @ Erfan) на groupby('name')[['val']].apply(calc).reset_index() вместо groupby('name')['val'].apply(list).reset_index() и функцию calc на:

def calc(data):
    p0 = np.array([data.val.values[0]])
    p1 = np.array([data.val.values[1]])
    p2 = np.array([data.val.values[2]])
    p3 = np.array([data.val.values[3]])


    data['calc'] = np.array([p0, p1, p2, p3])
    return data

и все работает отлично!

Ответы [ 2 ]

1 голос
/ 18 июня 2019

Метод 1

Вы можете связать groupby три раза с groupby.transform, groupby.head и groupby.sum:

df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)\
     .groupby('name', as_index=False).sum().drop('id', axis=1)


  name   val
0    a  0.84
1    b  2.68

Объяснение

  1. df[df.groupby('name')['id'].transform('count').ge(4)] возвращает все строки с уникальным именем, которые имеют 4 или более строк:
  name  id    val
0    a   0  0.100
1    a   1  0.200
2    a   2  0.020
3    a   3  0.520
4    a   4  0.017
5    b   0  0.870
6    b   1  0.240
7    b   2  0.670
8    b   3  0.900
  1. Затем мы объединяем его в цепочку .head(4), что дает нам только первые 4 строки в группе:
df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)

  name  id   val
0    a   0  0.10
1    a   1  0.20
2    a   2  0.02
3    a   3  0.52
5    b   0  0.87
6    b   1  0.24
7    b   2  0.67
8    b   3  0.90
  1. Наконец, мы получаем сумму первых 4 строк в группе с помощью .sum и удаляем столбец id:
df[df.groupby('name')['id'].transform('count').ge(4)]\
     .groupby('name').head(4)\
     .groupby('name', as_index=False).sum().drop('id', axis=1)

  name   val
0    a  0.84
1    b  2.68

Метод 2

Во многом аналогично первому способу, но затем с использованием groupby.filter:

df.groupby('name').filter(lambda x: x['id'].count() >= 4)\
  .groupby('name').head(4)\
  .groupby('name', as_index=False).sum().drop('id', axis=1)

  name   val
0    a  0.84
1    b  2.68

Метод 3

добавлено после комментария OP для применения пользовательской функции

Вы можете использовать .apply(list), чтобы получить первые 4 элемента в списке, к которому вы можете получить доступ:

df2 = df[df.groupby('name')['id'].transform('count').ge(4)]\
           .groupby('name').head(4)\
           .groupby('name')['val'].apply(list).reset_index()

  name                      val
0    a   [0.1, 0.2, 0.02, 0.52]
1    b  [0.87, 0.24, 0.67, 0.9]

Тогда, если вы хотите сложить эти значения:

df2['val'].apply(lambda x: sum(x))

0    0.84
1    2.68
Name: val, dtype: float64
0 голосов
/ 18 июня 2019

Если я правильно понял ваш вопрос, ниже вам должен помочь

grouped = df.groupby('name').filter(lambda x: x['name'].count() > 3)

for x in grouped['name'].unique():
    subf=df[df['name']==x]
    # you are still able to use the list
    #[0.1, 0.2, 0.02, 0.52, 0.017]
    #[0.87, 0.24, 0.67, 0.9]
    a=subf['val'].tolist()
    print(sum(a))
>> output
0.8570000000000001
2.6799999999999997
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...