Эквивалент макросов 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 (даже в определениях макросов) для двух целей:
- Подстановка текста
- Выражение оценки
Используя макросы, пользователь может упростить свой код, что, в свою очередь, уменьшит вероятность ошибок и сохранит порядок. Недостатком является то, что использование макросов отображает синтаксис языковой жидкости.
Чтобы ответить на ваш вопрос, 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