Группировка фреймов данных Pandas с применением функции с переменным количеством аргументов

У меня есть фрейм данных pandas, который выглядит так

import pandas as pd

data = {
  "Race_ID": [2,2,2,2,2,5,5,5,5,5,5],
  "Student_ID": [1,2,3,4,5,9,10,2,3,6,5],
  "theta": [8,9,2,12,4,5,30,3,2,1,50]
}

df = pd.DataFrame(data)

и у меня есть функция f(thetai, *theta) = thetai ** 2 + the other thetas in the same race, которую я хочу применить к столбцу theta в фрейме данных, сгруппированному по Race_ID, и создать новый столбец с именем feature.

Итак, у нас есть

Для ученика 1 в гонке 2 значение равно 8^2 + 9+2+12+4.

Для ученика 2 в гонке 2 значение равно 9^2 + 8+2+12+4.

Для ученика 3 в гонке 2 значение равно 2^2 + 8+9+12+4.

и т. д.

Я знаю о методах groupby и apply, но не знаю, как их применять, когда количество аргументов может меняться.

Итак, желаемый результат выглядит так

data = {
  "Race_ID": [2,2,2,2,2,5,5,5,5,5,5],
  "Student_ID": [1,2,3,4,5,9,10,2,3,6,5],
  "theta": [8,9,2,12,4,5,30,3,2,1,50],
  "fearure": [91,107,37,167,47,111,961,97,93,91,2541]
}

df = pd.DataFrame(data)

Обновлено: моя фактическая функция f для моей проблемы на самом деле намного сложнее, я просто использовал приведенный здесь пример, чтобы продемонстрировать основное препятствие, а именно проблему с переменным аргументом. Вот моя фактическая функция f:

def integrand(xi, thetai, *theta):
  S = 0
  for tj in theta:
    prod = 1
    for t in theta:
      if abs(t - tj) < 1e-10:
        continue
      prod = prod * (1 - norm.cdf(xi + thetai - t))
    S = S + norm.cdf(xi + thetai - tj) * prod
  return S * norm.pdf(xi)


def f(thetai, *theta):
  return (integrate.quad(integrand, -np.inf, np.inf, args=(thetai, *theta)))[0]
1
0
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Просто создайте промежуточный столбец сумм остальных тэт.

theta_sum = df.groupby("Race_ID").sum()["theta"].to_dict()
# (theta sum of race_id) - self theta
df["theta_feature"] = df.apply(
    lambda x: theta_sum[x["Race_ID"]] - x["theta"], axis=1
)
df["feature"] = df.apply(
    lambda x: x["theta"] ** 2 + x["theta_feature"], axis=1
)

а затем, если вам не нужен нежелательный столбец, вы можете просто удалить его с помощью

df.drop(columns = "theta_feature", inplace=True)

Обновлено: - Чтобы передать теты в функцию (которая даст другой результат для столбца feature, поскольку функция изначально не описана, вы можете сделать apply со следующим: -

def calc_grpd_thetas(x):
    _df = df[(df['Race_ID'] == x["Race_ID"]) & (df.index != x.name)]
    thetas = list(_df['theta'])
    return f(x["theta"], *thetas)

df["feature"] = df.apply(calc_grpd_thetas, axis=1)

что принесет вам это

Привет @dydev, спасибо за ответ, но моя фактическая функция f(thetai, *theta) на самом деле намного сложнее (это интеграл с использованием scipy.integrate.quad), и я просто использовал приведенный здесь пример, чтобы продемонстрировать основные препятствием, а именно проблемой переменного аргумента. Я отредактировал задачу, включив в нее свою настоящую функцию f, большое спасибо за вашу помощь.

Ishigami 25.07.2024 06:04

Привет, не могли бы вы включить функцию integrand в свое редактирование?

dydev 25.07.2024 11:54

Привет @dydev, я сделал, пожалуйста, посмотрите редактирование, большое спасибо ``` def integrand(xi, thetai, *theta): S = 0 для tj в тета: prod = 1 для t в тета: if abs(t - tj) < 1e-10: продолжить prod = prod * (1 -normal.cdf(xi + thetai - t)) S = S +normal.cdf(xi + thetai - tj) * prod return S *normal.pdf( xi) ```

Ishigami 25.07.2024 12:30

@Ishigami Я отредактировал сообщение, чтобы передать теты в функцию, как описано. Надеюсь, это было то, что вы искали.

dydev 25.07.2024 15:50

Используйте groupby.transform со специальной функцией:

def f(t):
    # get sum of all
    s = t.sum()
    # sum of all - self + self^2
    # = sum of others + self^2
    return t.rsub(s).add(t**2)

df['feature'] = df.groupby('Race_ID')['theta'].transform(f)

Это эквивалентно тому, чтобы взять сумму и добавить theta*(theta-1):

df['feature'] = (df.groupby('Race_ID')['theta']
                   .transform(lambda t: t*(t-1)+t.sum())
                 )

Альтернативно, с помощью transform('sum') и после этого выполните коррекцию.

df['feature'] = (df.groupby('Race_ID')['theta']
                   .transform('sum')
                   .sub(df['theta'])
                   .add(df['theta']**2)
                 )

Выход:

    Race_ID  Student_ID  theta  feature
0         2           1      8       91
1         2           2      9      107
2         2           3      2       37
3         2           4     12      167
4         2           5      4       47
5         5           9      5      111
6         5          10     30      961
7         5           2      3       97
8         5           3      2       93
9         5           6      1       91
10        5           5     50     2541

Привет @mozway, спасибо за ответ, но моя фактическая функция f(thetai, *theta) на самом деле намного сложнее (это интеграл с использованием scipy.integrate.quad), и я просто использовал приведенный здесь пример, чтобы продемонстрировать основные препятствием, а именно проблемой переменного аргумента. Я отредактировал задачу, включив в нее свою настоящую функцию f, большое спасибо за вашу помощь.

Ishigami 25.07.2024 06:05

Другие вопросы по теме