Новый столбец в фрейме данных Pandas с использованием метода наименьших квадратов из scipy.optimize

У меня есть фрейм данных Pandas, который выглядит следующим образом:

Race_ID   Date           Student_ID      feature1  
1         1/1/2023       3               0.02167131     
1         1/1/2023       4               0.17349148     
1         1/1/2023       6               0.08438952     
1         1/1/2023       8               0.04143787     
1         1/1/2023       9               0.02589056
1         1/1/2023       1               0.03866752     
1         1/1/2023       10              0.0461553     
1         1/1/2023       45              0.09212758
1         1/1/2023       23              0.10879326      
1         1/1/2023       102             0.186921      
1         1/1/2023       75              0.02990676      
1         1/1/2023       27              0.02731904      
1         1/1/2023       15              0.06020158      
1         1/1/2023       29              0.06302721                         
3         17/4/2022      5               0.2     
3         17/4/2022      2               0.1     
3         17/4/2022      3               0.55     
3         17/4/2022      4               0.15     

Я хотел бы создать новый столбец, используя следующий метод:

  1. Определите следующую функцию, используя интегралы
import numpy as np
from scipy import integrate
from scipy.stats import norm
import scipy.integrate as integrate
from scipy.optimize import fsolve
from scipy.optimize import least_squares

def integrandforpi_i(xi, ti, *theta):
  prod = 1
  for t in theta:
    prod = prod * (1 - norm.cdf(xi - t))
  return prod * norm.pdf(xi - ti)

def pi_i(ti, *theta):
  return integrate.quad(integrandforpi_i, -np.inf, np.inf, args=(ti, *theta))[0]
  1. для каждого Race_ID значение каждого Student_ID в новом столбце определяется путем решения системы нелинейных уравнений с использованием least_squares в scipy.optimize следующим образом:

Например, в забеге 1 участвуют 14 учеников, и нам придется решить следующую систему из 14 нелинейных уравнений, и мы ограничиваем границу между -2 и 2:

def equations(params):
    t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14 = params
    return (pi_i(t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) - 0.02167131,
            pi_i(t2,t1,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) - 0.17349148,
            pi_i(t3,t2,t1,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) - 0.08438952,
            pi_i(t4,t2,t3,t1,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14) - 0.04143787,
            pi_i(t5,t2,t3,t4,t1,t6,t7,t8,t9,t10,t11,t12,t13,t14) - 0.02589056,
            pi_i(t6,t2,t3,t4,t5,t1,t7,t8,t9,t10,t11,t12,t13,t14) - 0.03866752,
            pi_i(t7,t2,t3,t4,t5,t6,t1,t8,t9,t10,t11,t12,t13,t14) - 0.0461553,
            pi_i(t8,t2,t3,t4,t5,t6,t7,t1,t9,t10,t11,t12,t13,t14) - 0.09212758,
            pi_i(t9,t2,t3,t4,t5,t6,t7,t8,t1,t10,t11,t12,t13,t14) - 0.10879326,
            pi_i(t10,t2,t3,t4,t5,t6,t7,t8,t9,t1,t11,t12,t13,t14) - 0.186921,
            pi_i(t11,t2,t3,t4,t5,t6,t7,t8,t9,t10,t1,t12,t13,t14) - 0.02990676,
            pi_i(t12,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t1,t13,t14) - 0.02731904,
            pi_i(t13,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t1,t14) - 0.06020158,
            pi_i(t14,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t13,t14,t1) - 0.06302721)

res = least_squares(equations, (1,1,1,1,1,1,1,1,1,1,1,1,1,1), bounds = ((-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2,-2), (2,2,2,2,2,2,2,2,2,2,2,2,2,2)))

t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14 = res.x

Решение дает t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14 = [1.38473533 0.25616609 0.6935956 1.07314877 1.30201502 1.10781642 1.01839475 0.64349646 0.54630158 0.20719836 1.23347391 1.27642131 0.879412 0.83338882]

Аналогично, во втором забеге соревнуются 4 ученика, и нам придется решить следующую систему из 4 нелинейных уравнений:

def equations(params):
    t1,t2,t3,t4 = params
    return (pi_i(t1,t2,t3,t4) - 0.2,
            pi_i(t2,t1,t3,t4) - 0.1,
            pi_i(t3,t2,t1,t4) - 0.55,
            pi_i(t4,t2,t3,t1) - 0.15)

res = least_squares(equations, (1,1,1,1), bounds = ((-2,-2,-2,-2), (2,2,2,2)))

t1,t2,t3,t4 = res.x

что дает t1,t2,t3,t4 = [0.9209873 1.37615468 0.12293934 1.11735818].

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

Race_ID   Date           Student_ID      feature1       new_column
1         1/1/2023       3               0.02167131     1.38473533
1         1/1/2023       4               0.17349148     0.25616609
1         1/1/2023       6               0.08438952     0.6935956
1         1/1/2023       8               0.04143787     1.07314877
1         1/1/2023       9               0.02589056     1.30201502
1         1/1/2023       1               0.03866752     1.10781642
1         1/1/2023       10              0.0461553      1.01839475
1         1/1/2023       45              0.09212758     0.64349646
1         1/1/2023       23              0.10879326     0.54630158
1         1/1/2023       102             0.186921       0.20719836
1         1/1/2023       75              0.02990676     1.23347391
1         1/1/2023       27              0.02731904     1.27642131 
1         1/1/2023       15              0.06020158     0.879412 
1         1/1/2023       29              0.06302721     0.83338882                    
3         17/4/2022      5               0.2            0.9209873
3         17/4/2022      2               0.1            1.37615468
3         17/4/2022      3               0.55           0.12293934
3         17/4/2022      4               0.15           1.11735818

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

Насколько больше ваш фрейм данных?

jlandercy 04.08.2024 17:27

@jlandercy около 30 000 забегов с участием 5-20 учеников в каждом забеге.

Ishigami 04.08.2024 17:59
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
61
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Вот как параметризовать и автоматизировать регрессию для каждой группы.

Сначала мы загружаем ваш набор данных:

import io
import numpy as np
import pandas as pd
from scipy import integrate, stats, optimize

data = pd.read_fwf(io.StringIO("""Race_ID   Date           Student_ID      feature1  
1         1/1/2023       3               0.02167131     
1         1/1/2023       4               0.17349148     
1         1/1/2023       6               0.08438952     
1         1/1/2023       8               0.04143787     
1         1/1/2023       9               0.02589056
1         1/1/2023       1               0.03866752     
1         1/1/2023       10              0.0461553     
1         1/1/2023       45              0.09212758
1         1/1/2023       23              0.10879326      
1         1/1/2023       102             0.186921      
1         1/1/2023       75              0.02990676      
1         1/1/2023       27              0.02731904      
1         1/1/2023       15              0.06020158      
1         1/1/2023       29              0.06302721                         
3         17/4/2022      5               0.2     
3         17/4/2022      2               0.1     
3         17/4/2022      3               0.55     
3         17/4/2022      4               0.15   """))

Немного перепишем ваши функции:

def integrand(x, ti, *theta):
    product = 1.
    for t in theta:
        product = product * (1 - stats.norm.cdf(x - t))
    return product * stats.norm.pdf(x - ti)

def integral(ti, *theta):
    return integrate.quad(integrand, -np.inf, np.inf, args=(ti, *theta))[0]

Затем параметризуем систему уравнений:

def change_order(parameters, i):
    return [parameters[i]] + parameters[0:i] + parameters[i+1:]

def system(parameters, t):
    parameters = parameters.tolist()
    values = np.full(len(t), np.nan)
    for i in range(len(parameters)):
        parameters = change_order(parameters, i)
        values[i] = integral(*parameters) - t[i]
    return values

На этом этапе мы можем решить любую систему длины n, теперь создаём функцию, которая позволит нам использовать groupby в пандах:

def solver(x):
    t = x["feature1"].values
    u = np.ones_like(t)
    solution = optimize.least_squares(system, u, bounds=[-2*u, 2*u], args=(t,))
    return solution.x

И мы применим его к фрейму данных:

data["new_column"] = data.groupby("Race_ID").apply(solver, include_groups=False).explode().values

Что приводит к:

    Race_ID       Date  Student_ID  feature1 new_column
0         1   1/1/2023           3  0.021671   1.383615
1         1   1/1/2023           4  0.173491    0.25823
2         1   1/1/2023           6  0.084390   0.695116
3         1   1/1/2023           8  0.041438   1.073675
4         1   1/1/2023           9  0.025891   1.301445
5         1   1/1/2023           1  0.038668   1.108209
6         1   1/1/2023          10  0.046155   1.019114
7         1   1/1/2023          45  0.092128   0.645103
8         1   1/1/2023          23  0.108793   0.548053
9         1   1/1/2023         102  0.186921   0.209302
10        1   1/1/2023          75  0.029907    1.23329
11        1   1/1/2023          27  0.027319   1.276228
12        1   1/1/2023          15  0.060202   0.880537
13        1   1/1/2023          29  0.063027   0.855987
14        3  17/4/2022           5  0.200000   0.920987
15        3  17/4/2022           2  0.100000   1.376155
16        3  17/4/2022           3  0.550000   0.122939
17        3  17/4/2022           4  0.150000   1.117358

Действительно, вся операция выполняется довольно медленно для групп большой размерности.

Есть несколько вещей, которые вы можете сделать, чтобы ускорить весь процесс:

  • используйте цифру для шага решения;
  • распараллелить групповое решение;

Но это все равно тяжелая операция, если ваш набор данных огромен или имеет многомерные группы.

Большое вам спасибо за вашу помощь! Я никогда раньше не использовал нумбу, позвольте мне изучить, большое спасибо.

Ishigami 05.08.2024 12:47

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