estadistica.ar · Enciclopedia de Estadística en españolhttps://estadistica.ar/conceptos/estadistica-en-r-y-python
Estadística en R y Python: dónde está cada análisis y qué hace por defecto
La función de cada análisis en R y en Python, y los valores por defecto que hacen que el mismo análisis dé resultados distintos en uno y otro.
Por Jorge Luis Montivero, Licenciado en Estadística · Mu3Sigma
1 · Para qué es esta página
Para empezar a usar R o Python están primeros pasos en R y en Python. Esta página es para después, cuando ya se usa uno de los dos y hay que encontrar una función, traducir un análisis al otro lenguaje o entender por qué dos personas que hicieron «lo mismo» obtuvieron números distintos.
Casi siempre la respuesta a eso último es la misma: los dos programas calculan bien, pero por defecto no calculan lo mismo. Cada función tiene opciones que el que no las toca no ve, y en R y en Python esas opciones vienen puestas distinto.
El script que hace la comparación es datos/computo/preparar-r-y-python.py: corre
el lado de R con Rscript, calcula lo mismo en Python y se detiene si algo que
tiene que coincidir no coincide. Los resultados, con las versiones usadas,
quedan en datos/computo/r-y-python.json.
2 · Dónde está cada análisis
En Python se usan cuatro librerías: pandas para la tabla de datos,
SciPy (scipy.stats) para las pruebas, statsmodels para los modelos
y scikit-learn para el aprendizaje automático. En R casi todo viene en la
instalación base.
| Análisis | R | Python |
|---|---|---|
| Media, desvío | mean(x), sd(x) | df.x.mean(), df.x.std() |
| Cuantiles | quantile(x, 0.25) | df.x.quantile(0.25) |
| Tabla de frecuencias | table(x) | df.x.value_counts() |
| Tabla de contingencia | table(x, y) | pd.crosstab(df.x, df.y) |
| Prueba t, dos muestras | t.test(y ~ g) | stats.ttest_ind(a, b) |
| Prueba t pareada | t.test(a, b, paired = TRUE) | stats.ttest_rel(a, b) |
| Chi-cuadrado | chisq.test(tabla) | stats.chi2_contingency(tabla) |
| Exacta de Fisher | fisher.test(tabla) | stats.fisher_exact(tabla) |
| Intervalo para una proporción | prop.test(k, n), binom.test(k, n) | proportion_confint(k, n) |
| Mann-Whitney | wilcox.test(a, b) | stats.mannwhitneyu(a, b) |
| Correlación | cor.test(x, y) | stats.pearsonr(x, y), stats.spearmanr(x, y) |
| Regresión lineal | lm(y ~ x) | smf.ols("y ~ x", df).fit() |
| ANOVA | anova(lm(y ~ g)) | sm.stats.anova_lm(modelo) |
| Regresión logística | glm(y ~ x, family = binomial) | smf.logit("y ~ x", df).fit() |
| Normalidad | shapiro.test(x) | stats.shapiro(x) |
En la tabla, stats es scipy.stats, smf es statsmodels.formula.api, sm
es statsmodels.api y proportion_confint viene de
statsmodels.stats.proportion.
3 · Los valores por defecto que cambian el resultado
Ordenados de mayor a menor daño. Los primeros cambian la conclusión; los últimos, un decimal.
La regresión sin ordenada al origen
lm(y ~ x) en R y smf.ols("y ~ x", df) en Python incluyen la ordenada al
origen sin que nadie la pida. sm.OLS(y, x), la otra forma de statsmodels, no
la incluye: ajusta una recta obligada a pasar por cero, salvo que se agregue
una columna de unos con sm.add_constant(x).
Ingreso de la ocupación principal según edad, 594 ocupados:
| Pendiente | ||
|---|---|---|
R, lm(P21 ~ CH06) | $9.817 por año | 0,017 |
Python, smf.ols("P21 ~ CH06", df) | $9.817 por año | 0,017 |
Python, sm.OLS(df.P21, df.CH06) | $20.443 por año | 0,468 |
Welch o Student en la prueba t
t.test de R hace Welch por defecto: no supone varianzas iguales.
scipy.stats.ttest_ind hace Student por defecto: las supone iguales
(equal_var=True).
Ingreso de patrones contra asalariados. Son 41 patrones con un desvío de $2,23 millones contra 444 asalariados con un desvío de $0,77 millones:
| gl | |||
|---|---|---|---|
| R por defecto (Welch) | 1,91 | 40,9 | 0,064 |
| SciPy por defecto (Student) | 4,19 | 483 | 0,00003 |
Para obtener uno u otro en cualquiera de los dos: t.test(..., var.equal = TRUE)
en R da Student, y ttest_ind(..., equal_var=False) en SciPy da Welch. Welch es
la opción más segura cuando no hay buenas razones para suponer
varianzas iguales.
La logística que se encoge sola
glm en R y smf.logit en statsmodels estiman la
regresión logística por máxima verosimilitud.
LogisticRegression() de scikit-learn aplica por defecto una penalización
(L2, con C=1), que achica los coeficientes hacia cero. Es una
regresión penalizada aunque nadie lo haya
pedido.
Probabilidad de estar desocupado según edad, sexo y secundario completo, 655 activos con 26 desocupados:
| Coeficiente | R y statsmodels | scikit-learn por defecto |
|---|---|---|
| Ordenada | −1,038 | −1,140 |
| Edad | −0,036 | −0,035 |
| Mujer | −0,284 | −0,261 |
| Secundario completo | −0,962 | −0,825 |
El efecto del secundario aparece un 14 % más chico, y el odds ratio pasa de
0,38 a 0,44. Con LogisticRegression(penalty=None) scikit-learn coincide con R.
La regla práctica: para interpretar coeficientes, statsmodels; para predecir,
scikit-learn, sabiendo qué hace.
El ANOVA de dos factores depende del orden
anova(lm(...)) en R y anova_lm(...) en statsmodels usan sumas de cuadrados
de tipo I (secuenciales): cada factor se evalúa después de los que se
escribieron antes. Con grupos de distinto tamaño, el orden cambia el resultado.
Ingreso según sexo y nivel educativo (tres niveles), con celdas de entre 19 y 175 casos:
| Modelo | del sexo |
|---|---|
ingreso ~ sexo + nivel | 0,14 |
ingreso ~ nivel + sexo | 2,73 |
Las dos salidas son correctas: responden preguntas distintas. La primera
pregunta si el sexo importa sin tener en cuenta el nivel educativo. La
segunda, si importa a igual nivel. R y Python coinciden entre sí; el que da
distinto es SPSS, que usa por defecto el tipo III (2,73 en este caso, igual
que poner el sexo al final). En R se obtiene con car::Anova(modelo, type = 3)
y en Python con anova_lm(modelo, typ=3), en los dos casos con contrastes de
suma a cero. Ver ANOVA de dos factores.
El intervalo para una proporción
Cada función usa un método distinto, y ninguna lo dice en el nombre. Seis desocupados entre 62 jóvenes activos (9,7 %):
| Función | Método | Intervalo del 95 % |
|---|---|---|
proportion_confint(6, 62) | Wald (normal) | 2,3 % a 17,0 % |
proportion_confint(6, 62, method="wilson") | Wilson | 4,5 % a 19,5 % |
prop.test(6, 62) | Wilson con corrección por continuidad | 4,0 % a 20,5 % |
binom.test(6, 62) | Clopper-Pearson (exacto) | 3,6 % a 19,9 % |
El de statsmodels por defecto es el de Wald, el de los libros, y es el que peor funciona con proporciones chicas: queda corrido hacia abajo y cubre menos de lo que promete. Ver intervalo para una proporción.
Los valores perdidos
En la EPH, el ingreso que no se declaró viene como −9. Pasado a perdido, en los ocupados de Catamarca hay 30 de 631 (4,8 %):
| Media del ingreso | |
|---|---|
R, mean(x) | NA |
NumPy, np.mean(x) | nan |
pandas, df.x.mean() | $871.719 |
R y NumPy se niegan a calcular y obligan a decidir (na.rm = TRUE,
np.nanmean). pandas excluye los perdidos sin avisar, y lo mismo hacen
std, corr y casi todo lo demás. Es cómodo, pero la no respuesta pasa sin que
nadie la note, sea del 5 % como acá o del 30 %. Ver datos faltantes.
El desvío: n o n − 1
sd() en R y .std() en pandas dividen por , el
desvío muestral. np.std() de NumPy divide por
salvo que se le pase ddof=1. Sobre las edades de los primeros 12
ocupados del archivo:
| Desvío | |
|---|---|
R, pandas, np.std(x, ddof=1) | 14,62 años |
np.std(x) | 13,996 años |
Con muestras grandes la diferencia desaparece; con 12 casos es de 4 %.
Los cuartiles
R, pandas, NumPy y Excel usan por defecto el mismo método (el tipo 7 de la
clasificación de Hyndman y Fan). SPSS usa otro (el tipo 6). Sobre las mismas 12
edades, el primer cuartil da 30,75 en los primeros y 30,25 en SPSS. Para
reproducir a SPSS: quantile(x, 0.25, type = 6) en R y
np.percentile(x, 25, method="weibull") en NumPy. Ver
cuantiles.
El odds ratio de la prueba de Fisher
fisher.test de R informa el odds ratio condicional (un estimador de máxima
verosimilitud condicional). scipy.stats.fisher_exact informa el odds ratio
muestral, el de la cuenta a mano. En una tabla de desocupación por sexo entre
los jóvenes activos (33, 3, 23, 3), R da 1,426 y SciPy 1,435. El valor
es idéntico. Para obtener el condicional en Python está
odds_ratio(tabla), del módulo scipy.stats.contingency. Con tablas grandes los dos se
acercan; con tablas chicas pueden alejarse bastante. Ver
prueba exacta de Fisher.
Las que coinciden, aunque no se espere
Dos casos en los que R y Python sí hacen lo mismo por defecto, y lo que difiere es la cuenta del libro:
- Chi-cuadrado en tablas de 2×2.
chisq.testychi2_contingencyaplican la corrección de Yates. Sexo por secundario completo da 13,81 con corrección y 14,55 sin ella (correct = FALSE,correction=False). Quien lo calcula a mano obtiene el segundo y no entiende por qué el programa no coincide. - Redondeo de un 5 exacto.
round(2.5)da 2 en R y en Python, porque los dos redondean al par. Excel, conREDONDEAR, da 3. Una tabla armada con un programa y verificada con el otro puede diferir en el último dígito sin que haya ningún error. Ver redondeo.
4 · Cómo saber qué hizo una función
- Leer la firma, no el ejemplo.
help(t.test)en R ohelp(stats.ttest_ind)en Python muestran cada argumento con su valor por defecto.var.equal = FALSEyequal_var=Trueestán ahí, a la vista. - Mirar lo que la salida informa. R escribe «Welch Two Sample t-test» en el título; si la salida dice «gl = 40,9», los grados de libertad no enteros delatan a Welch. Ver cómo leer la salida.
- Probar con un caso conocido. Antes de confiar en una función nueva, correrla sobre un ejemplo del libro o de este sitio cuyo resultado se sabe.
- Escribir la opción aunque sea la por defecto.
t.test(..., var.equal = FALSE)dice lo mismo quet.test(...), pero quien lea el código, o quien lo traduzca al otro lenguaje, ya no tiene que adivinar.
Los análisis de la sección anterior, escritos así, dan lo mismo en los dos lenguajes:
t.test(P21 ~ grupo, data = d, var.equal = FALSE) # Welch
lm(P21 ~ CH06, data = d) # con ordenada al origen
glm(desoc ~ CH06 + mujer + sec, data = d, family = binomial)
quantile(d$CH06, 0.25, type = 7)
mean(d$P21, na.rm = TRUE) # decidido, no por omisión
stats.ttest_ind(a, b, equal_var=False) # Welch
smf.ols("P21 ~ CH06", data=d).fit() # con ordenada al origen
smf.logit("desoc ~ CH06 + mujer + sec", data=d).fit() # sin penalización
d.CH06.quantile(0.25, interpolation="linear")
d.P21.mean(skipna=True) # decidido, no por omisión
5 · Errores frecuentes
- Usar
sm.OLS(y, x)sinadd_constant. La recta pasa por cero y el sale inflado: 0,47 donde corresponde 0,02. - Traducir
t.testattest_indsinequal_var=False. Se pasa de Welch a Student sin darse cuenta. Con grupos desparejos, la conclusión puede cambiar. - Interpretar los coeficientes de
LogisticRegression()como los de una logística. Por defecto están penalizados y achicados. - Comparar un ANOVA de R o Python con uno de SPSS. Tipo I contra tipo III: con diseños no balanceados, las sumas de cuadrados no tienen por qué coincidir.
- Confiar en que pandas avise de los perdidos. No avisa: los saca. Contar
con
df.x.isna().sum()antes de describir. - Usar
proportion_confintsinmethod=. Devuelve Wald, que falla justamente con proporciones chicas y muestras chicas. - Pensar que un resultado distinto es un error. Antes de buscar el error, comparar los valores por defecto: casi siempre la diferencia está ahí.
Seguir leyendo
Bioestadística · Cómputo y comunicación · 285 de 322
Estadística para Economía y Administración · Cómputo y comunicación · 282 de 303
Probabilidad y Estadística · Cómputo y comunicación · 273 de 290
Cómo citar: Montivero, J. L. (2026). Estadística en R y Python: dónde está cada análisis y qué hace por defecto. estadistica.ar. https://estadistica.ar/conceptos/estadistica-en-r-y-python
estadistica.ar es gratuita y sin publicidad. Si te sirvió, podés invitar un cafecito por transferencia al alias mu3sigma. Asesoramiento estadístico, encuestas y capacitaciones: m3s.pro · WhatsApp +54 9 383 499-7929.
Cómo citar esta entrada
APA (7.ª edición)
Montivero, J. L. (2026). Estadística en R y Python: dónde está cada análisis y qué hace por defecto. estadistica.ar. https://estadistica.ar/conceptos/estadistica-en-r-y-python
BibTeX
@misc{estadistica_ar_estadistica_en_r_y_python,
author = {Montivero, Jorge Luis},
title = {{Estadística en R y Python: dónde está cada análisis y qué hace por defecto}},
year = {2026},
howpublished = {\url{https://estadistica.ar/conceptos/estadistica-en-r-y-python}},
note = {estadistica.ar. Consultado el \today},
urldate = {2026-10-01}
}Última actualización: 1 de octubre de 2026. El contenido está bajo licencia CC BY-NC-SA 4.0: se puede reutilizar citando la fuente y sin fines comerciales.