Эквивалент макросов Stata в Python

Я пытаюсь использовать Python для статистического анализа.

В Stata я могу определять локальные макросы и расширять их по мере необходимости:

program define reg2
    syntax varlist(min=1 max=1), indepvars(string) results(string)
    if "`results'" == "y" {
        reg `varlist' `indepvars'
    }
    if "`results'" == "n" {
        qui reg `varlist' `indepvars'
    }
end

sysuse auto, clear

Так что вместо:

reg2 mpg, indepvars("weight foreign price") results("y")

Я мог бы сделать:

local options , indepvars(weight foreign price) results(y) 
reg2 mpg `options'

Или даже:

local vars weight foreign price
local options , indepvars(`vars') results(y) 
reg2 mpg `options'

Макросы в Stata помогают мне писать чистые сценарии без повторения кода.

В Python я пробовал интерполяцию строк, но это не работает в функциях.

Например:

def reg2(depvar, indepvars, results):
    print(depvar)
    print(indepvars)
    print(results)

Следующее работает нормально:

reg2('mpg', 'weight foreign price', 'y')

Однако, оба из них терпят неудачу:

regargs = 'mpg', 'weight foreign price', 'y'
reg2(regargs)

regargs = 'depvar=mpg, covariates=weight foreign price, results=y'
reg2(regargs)

Я нашел похожий вопрос, но он не отвечает на мой вопрос:

Есть еще один вопрос по этому поводу для R:

Тем не менее, я не смог найти что-то для Python специально

Мне было интересно, есть ли в Python что-то похожее на макросы Stata?

2 ответа

Решение

Похоже, вы просто хотите * а также ** операторы для вызова функций:

regargs = 'mpg', 'weight foreign price', 'y'
reg2(*regargs)

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

Для вашего примера ключевого слова вам нужно немного изменить объявление:

regargs = dict(depvar='mpg', covariates='weight foreign price', results='y')
reg2(**regargs)

Сделай это по-Питонски.

Повсеместное использование макросов в Stata отражает другую философию программирования. В отличие от Python, который является объектно-ориентированным языком программирования общего назначения, Stata ado язык (не mata) требует макросов, чтобы функционировать как нечто большее, чем простой язык сценариев.

Макросы могут использоваться практически в любом месте в Stata (даже в определениях макросов) для двух целей:

  1. Подстановка текста
  2. Выражение оценки

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

Чтобы ответить на ваш вопрос, Pyexpander предоставляет некоторые из этих функций в Python, но на самом деле это не замена. Для разных вариантов использования вам понадобится другой подход к имитации макроса. В отличие от Stata, нет единого способа сделать это везде.

Мой совет, чтобы ознакомиться с соглашениями Python, а не пытаться программировать вещи "путь Stata". Например, полезно помнить, что локальные и глобальные макросы в Stata соответствуют переменным в Python (локальные в функции, глобальные снаружи), в то время как переменные в Stata соответствуют Pandas.Series или столбец Pandas.DataFrame, Точно так же Stata ado Программы соответствуют функциям в Python.

Решение, предоставленное в ответе @ gddc, может стать хорошим инструментом для достижения желаемого. Тем не менее, здесь необходимо выполнить дополнительные действия, если вы хотите повторно использовать свой код.

Используя ваш игрушечный пример:

import pandas as pd   
import numpy as np      
import statsmodels.api as sm

df = pd.read_stata('http://www.stata-press.com/data/r14/auto.dta')

In [1]: df[['mpg', 'weight', 'price']].head()
Out[1]: 
   mpg  weight  price
0   22    2930   4099
1   17    3350   4749
2   22    2640   3799
3   20    3250   4816
4   15    4080   7827

Предположим, вы хотите повторно использовать следующий фрагмент кода, но с другими переменными:

In [2]: Y = df['mpg']
In [3]: df['cons'] = 1
In [4]: X = df[['weight', 'price', 'cons']]

In [5]: reg = sm.OLS(Y, X).fit()
In [6]: print(reg.summary())
                            OLS Regression Results                            
==============================================================================
Dep. Variable:                    mpg   R-squared:                       0.653
Model:                            OLS   Adj. R-squared:                  0.643
Method:                 Least Squares   F-statistic:                     66.85
Date:                                   Prob (F-statistic):           4.73e-17
Time:                                   Log-Likelihood:                -195.22
No. Observations:                  74   AIC:                             396.4
Df Residuals:                      71   BIC:                             403.3
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
weight        -0.0058      0.001     -9.421      0.000      -0.007      -0.005
price      -9.351e-05      0.000     -0.575      0.567      -0.000       0.000
cons          19.7198      0.811     24.322      0.000      18.103      21.336
==============================================================================
Omnibus:                       29.900   Durbin-Watson:                   2.347
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               60.190
Skew:                           1.422   Prob(JB):                     8.51e-14
Kurtosis:                       6.382   Cond. No.                     1.50e+04
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.5e+04. This might indicate that there are
strong multicollinearity or other numerical problems.

Как ты мог это сделать?

Сначала создайте функцию:

def reg2(depvar, indepvars, results, df):
    Y = df[depvar]
    df['cons'] = 1
    X = df[indepvars]

    reg = sm.OLS(Y, X).fit()
    if results != 0:
        print(reg.summary())

Однако обратите внимание, что хотя интерполяция строк может "расширять" строки, здесь этот подход не будет работать, поскольку целевая функция для регрессионного анализа не принимает унифицированную строку вида 'weight, price, cons',

Вместо этого вам нужно определить список с регрессорами:

predictors = ['weight', 'price', 'cons']
reg2('mpg', predictors, 0, df)

Вы также можете перенести эту концепцию на следующий уровень, создав декоратор:

def load_and_reg2(func):
    def wrapper(*args, **kwargs):

        print()
        print("Loading the dataset...")
        print()

        df = pd.read_stata('http://www.stata-press.com/data/r14/auto.dta')
        sumvars = df[['mpg', 'weight', 'price']].head()
        print(sumvars)
        print()

        func(*args, **kwargs, df = df)
        return func(*args, **kwargs, df = df)

        print()
        print("Doing any other stuff you like...")
        print()

        dfshape = df.shape
        print('Shape:', dfshape)

    return wrapper

И используйте это в своем reg2() функция:

@load_and_reg2
def reg2(depvar, indepvars, results, df):
    Y = df[depvar]
    df['cons'] = 1
    X = df[indepvars]

    reg = sm.OLS(Y, X).fit()
    if results != 0:
        print(reg.summary())
    return reg

Пример, возможно, очень упрощенный, но демонстрирует мощь Python:

In [7]: [predictors = ['weight', 'price', 'cons']
In [8]: reg2('mpg', predictors, 1)

Loading the dataset...

   mpg  weight  price
0   22    2930   4099
1   17    3350   4749
2   22    2640   3799
3   20    3250   4816
4   15    4080   7827

                            OLS Regression Results                            
==============================================================================
Dep. Variable:                    mpg   R-squared:                       0.653
Model:                            OLS   Adj. R-squared:                  0.643
Method:                 Least Squares   F-statistic:                     66.85
Date:                                   Prob (F-statistic):           4.73e-17
Time:                                   Log-Likelihood:                -195.22
No. Observations:                  74   AIC:                             396.4
Df Residuals:                      71   BIC:                             403.3
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
==============================================================================
                 coef    std err          t      P>|t|      [0.025      0.975]
------------------------------------------------------------------------------
weight        -0.0058      0.001     -9.421      0.000      -0.007      -0.005
price      -9.351e-05      0.000     -0.575      0.567      -0.000       0.000
cons          39.4397      1.622     24.322      0.000      36.206      42.673
==============================================================================
Omnibus:                       29.900   Durbin-Watson:                   2.347
Prob(Omnibus):                  0.000   Jarque-Bera (JB):               60.190
Skew:                           1.422   Prob(JB):                     8.51e-14
Kurtosis:                       6.382   Cond. No.                     3.00e+04
==============================================================================

Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large,  3e+04. This might indicate that there are
strong multicollinearity or other numerical problems.

Doing any other stuff you like...

Shape: (74, 13)

Как вы можете видеть, декоратор дополнительно абстрагирует вещи, но используя фиксированный синтаксис.

В юниверсах Python словари и классы также играют важную роль в повторном использовании кода / результатов. Например, словарь может выступать в качестве эквивалента Stata return место для хранения нескольких макросов, скаляров и т. д.

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

def load_and_reg2(func):
    def wrapper(*args, **kwargs):

        D = {}

        print()   
        print("Loading the dataset...")
        print()

        df = pd.read_stata('http://www.stata-press.com/data/r14/auto.dta')
        sumvars = df[['mpg', 'weight', 'price']].head()
        D['sumvars'] = sumvars
        print(sumvars)
        print()

        D['reg2'] = func(*args, **kwargs, df)

        print()
        print("Doing any other stuff you like...")

        print()                     
        dfshape = df.shape
        D['dfshape'] = dfshape              
        print('Shape:', dfshape)

        return D    

    return wrapper

Вы можете легко сделать:

In [9]: foo = reg2('mpg', predictors, 1)

In [10]: foo.keys()
Out[10]: dict_keys(['sumvars', 'reg2', 'dfshape'])

In [11]: foo['sumvars']
Out[11]: 
   mpg  weight  price
0   22    2930   4099
1   17    3350   4749
2   22    2640   3799
3   20    3250   4816
4   15    4080   7827

Классы могут привнести дополнительную гибкость за счет некоторой дополнительной сложности:

class loadreg2return(object):
    def __init__(self, sumvars=None, reg2=None, dfshape=None):
        self.sumvars = sumvars
        self.reg2 = reg2
        self.dfshape = dfshape

def load_and_reg2(func):
    def wrapper(*args, **kwargs):

        print("Loading the dataset...")
        print()

        df = pd.read_stata('http://www.stata-press.com/data/r14/auto.dta')
        sumvars = df[['mpg', 'weight', 'price']].head()
        print(sumvars)
        print()

        reg2 = func(*args, **kwargs, df = df)

        print()
        print("Doing any other stuff you like...")

        print()                     
        dfshape = df.shape
        loadreg2return(dfshape = dfshape)            
        print('Shape:', dfshape)

        return loadreg2return(sumvars = sumvars, reg2 = reg2, dfshape = dfshape )

    return wrapper

Эта версия нашего игрушечного декоратора возвращает:

In [12]: foo.dfshape
Out[12]: (74, 13)

In [13]: foo.sumvars
Out[13]: 
   mpg  weight  price
0   22    2930   4099
1   17    3350   4749
2   22    2640   3799
3   20    3250   4816
4   15    4080   7827

In [14]: foo.reg2.params
Out[14]: 
weight    -0.005818
price     -0.000094
cons      39.439656

dtype: float64
Другие вопросы по тегам