options(java.parameters = "-Xmx10000m")
library(readxl)
library(tidyverse)
## ── Attaching packages ────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.0 ✓ purrr 0.3.4
## ✓ tibble 3.0.1 ✓ dplyr 0.8.5
## ✓ tidyr 1.0.2 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0
## ── Conflicts ───────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
Se reciben dos archivos, uno de entrada y uno de salida ejemplo.
El de entrada es: sitc315presdigit export-import usa vs all (2)_oea.xlsx
Y en este cuaderno lo llamaremos: base_de_datos
El de salida con el indice calculado por el investigador, para Antigua y Barbuda es: sitc315presdigit export-import usa vs all (2)_oea_pivote_1
Y en este cuaderno lo llamaremos: bd_indice_calculado
Se me solicita generalizar el cálculo del investigador a toda la base de datos del archivo de entrada.
Cargamos los dos archivos.
Leemos el archivo de entrada y lo ponemos en base_de_datos
base_de_datos <- read_excel("extdata/inputdata/sitc315presdigit export-import usa vs all (2)_oea.xlsx")
base_de_datos ahora contiene todo el archivo sobre el que debemos trabajar sus variables son las siguientes
names(base_de_datos)
## [1] "Year" "SITC"
## [3] "sitc_sdesc" "Country"
## [5] "ExportsFASValueBasisJan" "GenImportsCustomsValBasisJan"
## [7] "GenImportsCIFValBasisJan" "ExportsFASValueBasisFeb"
## [9] "GenImportsCustomsValBasisFeb" "GenImportsCIFValBasisFeb"
## [11] "ExportsFASValueBasisMar" "GenImportsCustomsValBasisMar"
## [13] "GenImportsCIFValBasisMar" "ExportsFASValueBasisApr"
## [15] "GenImportsCustomsValBasisApr" "GenImportsCIFValBasisApr"
## [17] "ExportsFASValueBasisMay" "GenImportsCustomsValBasisMay"
## [19] "GenImportsCIFValBasisMay" "ExportsFASValueBasisJun"
## [21] "GenImportsCustomsValBasisJun" "GenImportsCIFValBasisJun"
## [23] "ExportsFASValueBasisJul" "GenImportsCustomsValBasisJul"
## [25] "GenImportsCIFValBasisJul" "ExportsFASValueBasisAug"
## [27] "GenImportsCustomsValBasisAug" "GenImportsCIFValBasisAug"
## [29] "ExportsFASValueBasisSep" "GenImportsCustomsValBasisSep"
## [31] "GenImportsCIFValBasisSep" "ExportsFASValueBasisOct"
## [33] "GenImportsCustomsValBasisOct" "GenImportsCIFValBasisOct"
## [35] "ExportsFASValueBasisNov" "GenImportsCustomsValBasisNov"
## [37] "GenImportsCIFValBasisNov" "ExportsFASValueBasisDec"
## [39] "GenImportsCustomsValBasisDec" "GenImportsCIFValBasisDec"
## [41] "ExportsFASValueBasisYtdDec" "GenImportsCustomsValBasisYtdDec"
## [43] "GenImportsCIFValBasisYtdDec" "CTY_CODE"
Algunas entradas de esta base de datos:
base_de_datos%>%sample_n(10)
Hacemos lo mismo con el archivo de ejemplo, poniendolo en bd_indice_calculado:
bd_indice_calculado<-read_excel("extdata/outputdata/sitc315presdigit export-import usa vs all (2)_oea_pivote_1.xlsx")
## New names:
## * `IAEJUN2015/2016` -> `IAEJUN2015/2016...20`
## * `IAEJUN2015/2016` -> `IAEJUN2015/2016...25`
Este tiene las siguientes variables:
names(bd_indice_calculado)
## [1] "Year" "SITC"
## [3] "sitc_sdesc" "Country"
## [5] "GenImportsCIFValBasisJan" "GenImportsCIFValBasisFeb"
## [7] "GenImportsCIFValBasisMar" "GenImportsCIFValBasisApr"
## [9] "GenImportsCIFValBasisMay" "GenImportsCIFValBasisJun"
## [11] "GenImportsCIFValBasisJul" "GenImportsCIFValBasisAug"
## [13] "GenImportsCIFValBasisSep" "GenImportsCIFValBasisOct"
## [15] "GenImportsCIFValBasisNov" "GenImportsCIFValBasisDec"
## [17] "CTY_CODE" "Media"
## [19] "SD" "IAEJUN2015/2016...20"
## [21] "IAEFEB2015/2016" "IAEMAR2015/2016"
## [23] "IAEAPR2015/2016" "IAEMAY2015/2016"
## [25] "IAEJUN2015/2016...25" "IAEJUL2015/2016"
## [27] "IAEAUG2015/2016" "IAESEP2015/2016"
## [29] "IAEOCT2015/2016" "IAENOV2015/2016"
## [31] "IAEDIC2015/2016"
Algunas entradas de este archivo de ejemplo son:
bd_indice_calculado %>% sample_n(10)
En bd_indice_calculado se observan variables con nombre IAE* que contienen el Indice de Anomalía Estadística calculado por el investigador. Un vistazo a la hoja de calculo, arroja el método de calculo. Por cada renglón:
El indice de anomalías estadisticas para un renglon identificado por (AÑO_ACTUAL,pais,producto), se calcula así para cada mes, exclusivamente sobre las variables que se llaman GenImportsCIFValBasisMES:
Por ejemplo, el valor de IAEJAN2015/2016 de Antigua y Barbuda para el producto 931 se calcula:
El resultado de este cuaderno, debe arrojar un archivo con el IAE para todos los meses de todos los años, productos y paises.
En esta sección demostramos que podemos hacer el cálculo requerido para el IAE que se compone de media, desviación estandar y otros factores, de forma equivalente al calculado por el investigador en su archivo de ejemplo.
Posteriormente, procederemos a calcular el IAE para todos los renglones identificados por las variables: año,país,producto.
Extraemos unicamente las columnas que nos interesan y ponemos el resultado en otro nombre para distinguirlo. Será base_de_datos_trans:
base_de_datos%>%
select(Year,sitc_sdesc,SITC,Country,contains('GenImportsCIFValBasis'))%>%
select(-contains('YTD'))->base_de_datos_trans
base_de_datos_trans
Calculamos media y desviacion estandar y la ponemos en otra variable base_de_datos_trans_md_ds.
NOTA: Un subproducto de esta operación es una versión de la base de datos que extrae los meses del nombre de las variables y los pone en una columna. Esta se llama base_de_datos_trans_longer y será útil posteriormente.
base_de_datos_trans%>%
pivot_longer(contains("GenImportsCIFValBasis"),
names_to = "Month",
values_to = "GenImportsCIFValBasis",
names_prefix = "GenImportsCIFValBasis") ->base_de_datos_trans_longer
base_de_datos_trans_longer%>%
group_by(Year, sitc_sdesc,SITC,Country)%>%
summarize(
Media=mean(GenImportsCIFValBasis),
SD=sd(GenImportsCIFValBasis)) -> base_de_datos_trans_md_ds
base_de_datos_trans_md_ds
Si hicimos el calculo correcto, la Media y Desviacion estandar calculada por el investigador en su archivo, cuyo contenido está en bd_indice_calculado debe ser la misma que nosotros calculamos en base_de_datos_trans_md_ds.
El siguiente código en R extrae de bd_indice_calculado los renglones que tienen datos en las columnas SD y Media. Luego busca, en base_de_datos_trans_md_ds, utilizando el año, el codigo de producto (SITC) y el país, el renglon que corresponde al año, producto y país.
El resultado es una matriz con la que podemos ver lado a lado el valor del investigador vs el valor que calculamos aquí. Si nuestros calculos son validos, los valores de media y desviación estándar deben ser idénticos o equivalentes:
bd_indice_calculado%>%
filter((!is.na(SD)) & (!is.na(Media)) & (SD!=0 & Media != 0))%>%
select(Year,Country,SITC,Media,SD)%>%
inner_join(base_de_datos_trans_md_ds,suffix = c("_VALOR_INVESTIGADOR","_VALOR_CALCULADO"),
by=c("Year","SITC","Country"))%>%
select(Year,Country,SITC,sitc_sdesc,contains("Media"),contains("SD"))
La comparación manual/visual en este cuaderno para un primer vistazo es valiosa, pero lo mejor es que el codigo nos diga si alguno de estos valores son distintos. En la siguiente tabla introducimos dos nuevas columnas MediaDif y SDDif. Sus valores resultan de restar al valor en el archivo del investigador, con el calculado por nosotros:
bd_indice_calculado%>%
filter((!is.na(SD)) & (!is.na(Media)) & (SD!=0 & Media != 0))%>%
select(Year,Country,SITC,Media,SD)%>%
inner_join(base_de_datos_trans_md_ds,suffix = c("_VALOR_INVESTIGADOR","_VALOR_CALCULADO"),
by=c("Year","SITC","Country"))%>%
select(Year,Country,SITC,sitc_sdesc,contains("Media"),contains("SD"))%>%
#Hasta esta linea, es el mismo código que el anterior. A partir de aqui,
#creamos una columna que nos dice si los valores difieren:
mutate(
MediaDif=(Media_VALOR_INVESTIGADOR - Media_VALOR_CALCULADO),
SDDif=(SD_VALOR_INVESTIGADOR - SD_VALOR_CALCULADO)
)
Las columnas MediaDif y SDDif de esta tabla deben ser Cero o muy cercanas a cero para dar por válidos los cálculos. Esto es porque sus valores resultan de restar lo que nos ofrece el investigador en su archivo con lo que nosotros calculamos.
Si exploramos usando las flechas de navegación de la tabla, los valores de MediaDif son cero pero, si miramos la columna SDDif, existen algunos valores distintos de cero.
A simple vista, parecen ser errores pequeñísimos, quizás diferencias de minima presición numérica entre excel (con lo que el investigador calculó) y R (que es con lo que calculamos nosotros).
Veamos exactamente el rango de diferencias para salir de dudas aplicando la función summary, que nos da las características estadísticas de cada una de las columnas de la tabla:
bd_indice_calculado%>%
filter((!is.na(SD)) & (!is.na(Media)) & (SD!=0 & Media != 0))%>%
select(Year,Country,SITC,Media,SD)%>%
inner_join(base_de_datos_trans_md_ds,suffix = c("_VALOR_INVESTIGADOR","_VALOR_CALCULADO"),
by=c("Year","SITC","Country"))%>%
select(Year,Country,SITC,sitc_sdesc,contains("Media"),contains("SD"))%>%
mutate(
MediaDif=(Media_VALOR_INVESTIGADOR - Media_VALOR_CALCULADO),
SDDif=(SD_VALOR_INVESTIGADOR - SD_VALOR_CALCULADO)
#Hasta esta linea, es el mismo código que el anterior. A partir de aqui,
#usamos la función summary para conocer los rangos de todas las variables
#de esta tabla
)%>%summary()
## Year Country SITC sitc_sdesc
## Length:31 Length:31 Length:31 Length:31
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Media_VALOR_INVESTIGADOR Media_VALOR_CALCULADO SD_VALOR_INVESTIGADOR
## Min. : 25.3 Min. : 25.3 Min. : 87.8
## 1st Qu.: 255.4 1st Qu.: 255.4 1st Qu.: 884.6
## Median : 1637.7 Median : 1637.7 Median : 4394.5
## Mean : 18736.1 Mean : 18736.1 Mean : 30981.7
## 3rd Qu.: 6211.1 3rd Qu.: 6211.1 3rd Qu.: 11868.6
## Max. :473644.9 Max. :473644.9 Max. :748089.8
## SD_VALOR_CALCULADO MediaDif SDDif
## Min. : 87.8 Min. :0 Min. :-1.164e-10
## 1st Qu.: 884.6 1st Qu.:0 1st Qu.: 0.000e+00
## Median : 4394.5 Median :0 Median : 0.000e+00
## Mean : 30981.7 Mean :0 Mean :-3.803e-12
## 3rd Qu.: 11868.6 3rd Qu.:0 3rd Qu.: 0.000e+00
## Max. :748089.8 Max. :0 Max. : 1.819e-12
Si vemos la entrada correspondiente a SDDif, encontramos que la diferencia más grande en SD es 1.819x10^(-12) que es, para efectos prácticos, ninguna.
Con todo lo anterior damos por bueno el cálculo de desviación estándar y promedio porque resulta idéntico al calculado por el investigador como lo ofrece en su archivo ejemplo.
Para calcular el IAE, requerimos, para cada renglón en la base de datos, buscar el valor de ese producto y mes en el año siguiente y restar ese valor a la Media para el año del renglon actual. Eso se divide entre la desviación estándar del renglon actual.
base_de_datos_trans_longer%>%ungroup()%>%
mutate(next_Year=as.character(as.numeric(Year)+1)) %>%
inner_join(
base_de_datos_trans_longer,
by=c("next_Year"="Year",
"Country",
"SITC",
"sitc_sdesc",
"Month"),
suffix=c("_currentYear","_nextYear")
)%>%
inner_join(base_de_datos_trans_md_ds,
by=c("Year",
"Country",
"SITC",
"sitc_sdesc"))%>%
mutate(IAE=(GenImportsCIFValBasis_nextYear-Media)/SD) -> With_IAE
With_IAE
Esta nueva base de datos, tiene columnas para el Year (año actual), Month (el mes para el que se calcula),next_Year (el año actual + 1), GenImportsCIFValBasis_currentYear (el valor buscado para el año actual), GenImportsCIFValBasis_nextYear (el valor buscado para el siguiente año).
Adicionalmente, incluye las columnas Media y SD calculadas y verificadas anteriormente.
Con estas columnas, el calculo del IAE es directo. Para verificar que es correcto, busquemos correspondencias de valor del IAE calculado aqui vs. las que el investigador ya calculó.
Lo primero es filtrar el archivo del investigador bd_indice_calculado para quedarnos unicamente con renglones para los que se haya calculado el IAE
bd_indice_calculado%>%
filter_at(vars(contains("IAE")),
.vars_predicate = any_vars(!is.na(.)))->bd_indice_calculado_filtrado
bd_indice_calculado_filtrado
Este conjunto de datos solo tiene 31 renglones que son los que tienen el calculo de IEA del investigador, dispuestos en columnas de nombre IEAMESAÑO_ACTUAL/AÑO_SIGUIENTE
Para verificar que nuestro IEA calculado coincide con el del investigador, necesitamos que coincidan en formato. Tomamos la tabla del investigador y la convertimos a formato largo, igual que la que tenemos para nuestros valores calculados: con una columna de Month y una sola columna de IAE para el valor del indice. Esto nos permitirá hacer una comparación lado a lado:
bd_indice_calculado_filtrado%>%
pivot_longer(
matches("IAE"),
names_pattern = "IAE(...)(....)\\/(....)",
names_to=c("Month","current_Year","next_Year"),
values_to = "IAE")%>%
select(-current_Year,-next_Year,-matches("GenImportsCIFValBasis"))->bd_indice_calculado_filtrado_longer
bd_indice_calculado_filtrado_longer
Esta tabla tiene una columna IAE que tiene, sin haber sido modificados, los valores como los calculo el investigador en excel.
Usando esta tabla y la que nosotros calculamos aquí previamente, podemos comparar todos los valores y asegurarnos que nuestro algoritmo coincide con el del investigador:
With_IAE%>%
mutate(Month=toupper(Month))%>%
inner_join(bd_indice_calculado_filtrado_longer,
by=c("Year","Country","SITC","Month","sitc_sdesc"),
suffix=c("_NUESTRO_IAE","_INVESTIGADOR_IAE"))->lado_a_lado
lado_a_lado%>%
select(Year,SITC,Country,Month,IAE_NUESTRO_IAE,IAE_INVESTIGADOR_IAE,GenImportsCIFValBasis_currentYear)
En la tabla previa se puede comparar las columnas IAE_NUESTRO_IAE con IAE_INVESTIGADOR_IAE y deben ser identicas. DE la misma forma que con la media y el sd, haremos una comparación de diferencias:
lado_a_lado%>%
mutate(IAE_DIF=IAE_INVESTIGADOR_IAE-IAE_NUESTRO_IAE)%>%
filter(IAE_DIF!=0)
Existen aparentemente algunas diferencias importantes en el calculo de IAE. Luego de cuidadosa investigación, encontramos el problema. En el archivo del investigador el IAE de cada producto,año,pais, se codifica en columnas con nombre: IAEMESAÑO_ACTUAL/AÑO_SIGIENTE, pero hay un error de codificación. Veamos los nombres de esas columnas:
bd_indice_calculado%>%
select(matches("IAE"))%>%names()
## [1] "IAEJUN2015/2016...20" "IAEFEB2015/2016" "IAEMAR2015/2016"
## [4] "IAEAPR2015/2016" "IAEMAY2015/2016" "IAEJUN2015/2016...25"
## [7] "IAEJUL2015/2016" "IAEAUG2015/2016" "IAESEP2015/2016"
## [10] "IAEOCT2015/2016" "IAENOV2015/2016" "IAEDIC2015/2016"
Como se observa, IAEJUN está repetido de origen. Checando el archivo original, encontramos que la columna donde se calcula el mes de Enero (JAN), que aparece bajo la columna T tiene el nombre JUN. Un error de dedo común.
Por tanto, en el siguiente codigo volvemos a cargar el archivo recodificando ese nombre a JAN, recreamos las tablas intermedias necesarias para hacer la comparación y volvemos a comparar con nuestros propios calculos:
bd_indice_calculado<-read_excel("extdata/outputdata/sitc315presdigit export-import usa vs all (2)_oea_pivote_1.xlsx",
)%>%
rename(`IAEJAN2015/2016`=`IAEJUN2015/2016...20`,
`IAEJUN2015/2016`=`IAEJUN2015/2016...25`)
## New names:
## * `IAEJUN2015/2016` -> `IAEJUN2015/2016...20`
## * `IAEJUN2015/2016` -> `IAEJUN2015/2016...25`
# bd_indice_calculado%>%
# filter_at(vars(contains("IAE")),
# .vars_predicate = any_vars(!is.na(.)))->bd_indice_calculado_filtrado
bd_indice_calculado->bd_indice_calculado_filtrado
bd_indice_calculado_filtrado%>%
pivot_longer(
matches("IAE"),
names_pattern = "IAE(...)(....)\\/(....)",
names_to=c("Month","current_Year","next_Year"),
values_to = "IAE")%>%
select(-current_Year,-next_Year,-matches("GenImportsCIFValBasis"))->bd_indice_calculado_filtrado_longer
With_IAE%>%
mutate(Month=toupper(Month))%>%
inner_join(bd_indice_calculado_filtrado_longer,
by=c("Year","Country","SITC","Month","sitc_sdesc"),
suffix=c("_NUESTRO_IAE","_INVESTIGADOR_IAE"))->lado_a_lado
lado_a_lado%>%
select(Year,SITC,Country,Month,IAE_NUESTRO_IAE,IAE_INVESTIGADOR_IAE,GenImportsCIFValBasis_currentYear)%>%
filter(!is.na(IAE_INVESTIGADOR_IAE) & (!is.nan(IAE_NUESTRO_IAE)) & (!is.infinite(IAE_NUESTRO_IAE)))
A diferencia del investigador, nuestro algoritmo distingue entre IAE que dio infinito (por división por cero), IAE que dio No Numerico (NaN: por division de cero entre cero). Por ello filtramos aquellos renglones en los que nuestro calculo cae en estos dos valores. De este resultado, si podemos hacer un análisis de diferencias:
lado_a_lado%>%
select(Year,SITC,Country,Month,IAE_NUESTRO_IAE,IAE_INVESTIGADOR_IAE,GenImportsCIFValBasis_currentYear)%>%
filter(!is.na(IAE_INVESTIGADOR_IAE) & (!is.nan(IAE_NUESTRO_IAE)) & (!is.infinite(IAE_NUESTRO_IAE)))%>%
mutate(IAE_DIF=IAE_NUESTRO_IAE-IAE_INVESTIGADOR_IAE)
Y ahora, filtremos todos los resultados cuya diferencia en IAE no sea zero:
lado_a_lado%>%
select(Year,SITC,Country,Month,IAE_NUESTRO_IAE,IAE_INVESTIGADOR_IAE,GenImportsCIFValBasis_currentYear)%>%
filter(!is.na(IAE_INVESTIGADOR_IAE) & (!is.nan(IAE_NUESTRO_IAE)) & (!is.infinite(IAE_NUESTRO_IAE)))%>%
mutate(IAE_DIF=IAE_NUESTRO_IAE-IAE_INVESTIGADOR_IAE)%>%
filter(IAE_DIF!=0)
Esto nos sigue dejando 53 renglones donde los resultados no son idénticos. A simple vista, algunos de ellos son significativos pero otros no. Quitemos todos los que la diferencia absoluta sea mayor a 14 digitos de precisión.
lado_a_lado%>%
select(Year,SITC,Country,Month,IAE_NUESTRO_IAE,IAE_INVESTIGADOR_IAE,GenImportsCIFValBasis_currentYear)%>%
filter(!is.na(IAE_INVESTIGADOR_IAE) & (!is.nan(IAE_NUESTRO_IAE)) & (!is.infinite(IAE_NUESTRO_IAE)))%>%
mutate(IAE_DIF=IAE_NUESTRO_IAE-IAE_INVESTIGADOR_IAE)%>%
filter(IAE_DIF!=0 & (abs(IAE_DIF)>1.0e-14))
Y aqui ya tenemos las unicas verdaderas inconsistencias. Aparecen solo en el producto 112, en todos los meses, y el calculo de IAE es muy raro en nuestro caso puesto que nos dá -0.3864848 para todos los meses excepto noviembre, mientras que el investigador encontró IAE completos para todos los meses.
Investigando a fondo el archivo del investigador, encontré que la fórmula de cálculo de IEA que utiliza para el producto 112 en el año 2015, al intentar buscar el valor del mes correspondiente al año siguiente (2016), hace referencia a la celda en el excel E914. La formula es: =(E914-$R\(141)/\)S$141.
Al buscar esta celda, que deberia ser el producto 112 para Antigua y Barbuda en el 2016, me encuentro que se trata de un renglon que no tiene relación alguna, como puede verse en este screenshot:
#
Por tanto, damos por bueno nuestro cálculo y nos disponemos a generar el archivo de excel con la totalidad del IAE calculado con meses en una columna Month y una versión con el formato que el investigador nos dio: meses codificados en el nombre de las columnas IEA.
library(haven)
cat("\nGuardando version STATA IAE_Month_Column.dta\n")
##
## Guardando version STATA IAE_Month_Column.dta
haven::write_dta(With_IAE,"./IAE_Month_Column.dta")
cat("Guardando version SPSS IAE_Month_Column.sav\n")
## Guardando version SPSS IAE_Month_Column.sav
haven::write_sav(With_IAE,"./IAE_Month_Column.sav")
cat("Guardando version CSV IAE_Month_Column.csv\n")
## Guardando version CSV IAE_Month_Column.csv
write_excel_csv2(With_IAE,path = "./IAE_Month_Column.csv")
cat("\nExpandiendo meses a columnas\n")
##
## Expandiendo meses a columnas
With_IAE%>%
select(-matches("GenImports"))%>%
pivot_wider(names_from = c("Month","Year","next_Year"),
values_from="IAE",
names_prefix="IAE")->With_IAE_wider
cat("Guardando version STATA IAE_Wider.dta\n")
## Guardando version STATA IAE_Wider.dta
haven::write_dta(With_IAE,"./IAE_Wider.dta")
cat("Guardando version SPSS IAE_Wider.sav\n")
## Guardando version SPSS IAE_Wider.sav
haven::write_sav(With_IAE,"./IAE_Wider.sav")
cat("Guardando version CSV IAE_Wider.csv\n")
## Guardando version CSV IAE_Wider.csv
write_excel_csv2(With_IAE,path = "./IAE_Wider.csv")
cat("\nCopiando archivos a dropbox\n")
##
## Copiando archivos a dropbox
f<-list.files(".",pattern="IAE_.*")
file.copy(f,"/home/alex/Dropbox/COVID-data/IMPORTSUSA")
## [1] FALSE FALSE FALSE FALSE FALSE FALSE
Puedes consultar los archivos cargados en el dropbox