{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Running Python, R or C Code from Julia\n",
    "\n",
    "This notebook provides a basic introduction to how to run Python, R and C code from Julia."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "printyellow (generic function with 1 method)"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using Printf, DelimitedFiles\n",
    "include(\"jlFiles/printmat.jl\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Load Data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OLS coeffs according to Julia\n",
      "    -0.504\n",
      "     1.341\n",
      "\n"
     ]
    }
   ],
   "source": [
    "x = readdlm(\"Data/MyData.csv\",',',skipstart=1)  #reading the csv file\n",
    "\n",
    "(Rme,Rf,R) = (x[:,2],x[:,3],x[:,4])  #creating variables from columns of x\n",
    "y  = R - Rf                          #do R .- Rf if R has several columns\n",
    "\n",
    "c = ones(length(Rme))\n",
    "x = [c Rme]\n",
    "\n",
    "b = x\\y\n",
    "println(\"OLS coeffs according to Julia\")\n",
    "printmat(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python\n",
    "\n",
    "using the [PyCall.jl](https://github.com/JuliaPy/PyCall.jl) package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "PyObject <module 'statsmodels.api' from 'C:\\\\Miniconda3\\\\lib\\\\site-packages\\\\statsmodels\\\\api.py'>"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "using PyCall\n",
    "sm = pyimport(\"statsmodels.api\")     #activate this package"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "PyObject <class 'statsmodels.iolib.summary.Summary'>\n",
      "\"\"\"\n",
      "                            OLS Regression Results                            \n",
      "==============================================================================\n",
      "Dep. Variable:                      y   R-squared:                       0.519\n",
      "Model:                            OLS   Adj. R-squared:                  0.518\n",
      "Method:                 Least Squares   F-statistic:                     416.2\n",
      "Date:                Fri, 16 Jul 2021   Prob (F-statistic):           2.72e-63\n",
      "Time:                        15:04:44   Log-Likelihood:                -1241.7\n",
      "No. Observations:                 388   AIC:                             2487.\n",
      "Df Residuals:                     386   BIC:                             2495.\n",
      "Df Model:                           1                                         \n",
      "Covariance Type:            nonrobust                                         \n",
      "==============================================================================\n",
      "                 coef    std err          t      P>|t|      [0.025      0.975]\n",
      "------------------------------------------------------------------------------\n",
      "const         -0.5042      0.305     -1.654      0.099      -1.103       0.095\n",
      "x1             1.3410      0.066     20.401      0.000       1.212       1.470\n",
      "==============================================================================\n",
      "Omnibus:                      259.682   Durbin-Watson:                   1.870\n",
      "Prob(Omnibus):                  0.000   Jarque-Bera (JB):             5249.944\n",
      "Skew:                           2.482   Prob(JB):                         0.00\n",
      "Kurtosis:                      20.323   Cond. No.                         4.68\n",
      "==============================================================================\n",
      "\n",
      "Notes:\n",
      "[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.\n",
      "\"\"\"\n"
     ]
    }
   ],
   "source": [
    "resultsP = sm.OLS(y, x).fit()        #can use Python functions directly\n",
    "\n",
    "println(resultsP.summary())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[:HC0_se, :HC1_se, :HC2_se, :HC3_se, :_HCCM, :__class__, :__delattr__, :__dict__, :__dir__, :__doc__, :__eq__, :__format__, :__ge__, :__getattribute__, :__gt__, :__hash__, :__init__, :__init_subclass__, :__le__, :__lt__, :__module__, :__ne__, :__new__, :__reduce__, :__reduce_ex__, :__repr__, :__setattr__, :__sizeof__, :__str__, :__subclasshook__, :__weakref__, :_abat_diagonal, :_cache, :_data_attr, :_data_in_cache, :_get_robustcov_results, :_is_nested, :_use_t, :_wexog_singular_values, :aic, :bic, :bse, :centered_tss, :compare_f_test, :compare_lm_test, :compare_lr_test, :condition_number, :conf_int, :conf_int_el, :cov_HC0, :cov_HC1, :cov_HC2, :cov_HC3, :cov_kwds, :cov_params, :cov_type, :df_model, :df_resid, :diagn, :eigenvals, :el_test, :ess, :f_pvalue, :f_test, :fittedvalues, :fvalue, :get_influence, :get_prediction, :get_robustcov_results, :initialize, :k_constant, :llf, :load, :model, :mse_model, :mse_resid, :mse_total, :nobs, :normalized_cov_params, :outlier_test, :params, :predict, :pvalues, :remove_data, :resid, :resid_pearson, :rsquared, :rsquared_adj, :save, :scale, :ssr, :summary, :summary2, :t_test, :t_test_pairwise, :tvalues, :uncentered_tss, :use_t, :wald_test, :wald_test_terms, :wresid]\n"
     ]
    }
   ],
   "source": [
    "println(keys(resultsP))              #print all keys (field names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Comparing the estimates in Julia and Python\n",
      "    -0.504    -0.504\n",
      "     1.341     1.341\n",
      "\n"
     ]
    }
   ],
   "source": [
    "b_P = resultsP.params                #the numerical results are now a Julia vector\n",
    "\n",
    "println(\"Comparing the estimates in Julia and Python\")\n",
    "printmat([b b_P])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[-0.5041626   1.34104865]\r\n"
     ]
    }
   ],
   "source": [
    "#we can run blocks of code like this, notice: $x and $y\n",
    "py\"\"\"\n",
    "  import numpy as np\n",
    "  xx = np.matmul(np.matrix.transpose($x),$x)\n",
    "  xy = np.matmul(np.matrix.transpose($x),$y)\n",
    "  b_p = np.linalg.solve(xx,xy)\n",
    "  #b_p = np.linalg.lstsq($x,$y,rcond=None)\n",
    "  print(b_p)\n",
    "  \"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# R\n",
    "\n",
    "using the [RCall.jl](https://github.com/JuliaInterop/RCall.jl) package."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "ENV[\"R_HOME\"]=\"C:/PROGRA~1/R/R-40~1.4\"    #path to R, do R.home() in R to see\n",
    "using RCall                               #do import Pkg; Pkg.build(\"RCall\") after changing R location "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RObject{VecSxp}\n",
      "\n",
      "Call:\n",
      "lm(formula = y ~ x - 1)\n",
      "\n",
      "Residuals:\n",
      "    Min      1Q  Median      3Q     Max \n",
      "-17.981  -3.131  -0.359   2.281  52.361 \n",
      "\n",
      "Coefficients:\n",
      "   Estimate Std. Error t value Pr(>|t|)    \n",
      "x1 -0.50416    0.30483  -1.654    0.099 .  \n",
      "x2  1.34105    0.06573  20.401   <2e-16 ***\n",
      "---\n",
      "Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1\n",
      "\n",
      "Residual standard error: 5.954 on 386 degrees of freedom\n",
      "Multiple R-squared:  0.5194,\tAdjusted R-squared:  0.5169 \n",
      "F-statistic: 208.6 on 2 and 386 DF,  p-value: < 2.2e-16\n",
      "\n",
      "\n",
      "[:coefficients, :residuals, :effects, :rank, Symbol(\"fitted.values\"), :assign, :qr, Symbol(\"df.residual\"), :xlevels, :call, :terms, :model]\n"
     ]
    }
   ],
   "source": [
    "@rput x y                          #send x and y to R\n",
    "\n",
    "resultsR = reval(\"summary(mod <- lm(y ~ x-1))\")  #run R code and print output\n",
    "println(resultsR)\n",
    "\n",
    "resultsR = reval(\"mod <- lm(y ~ x-1)\")  #run R code\n",
    "\n",
    "println(names(resultsR))           #print all keys (field names)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Comparing the estimates in Julia and R\n",
      "    -0.504    -0.504\n",
      "     1.341     1.341\n",
      "\n"
     ]
    }
   ],
   "source": [
    "b_R = rcopy(resultsR[:coefficients])  #the numerical results are now a Julia array\n",
    "\n",
    "println(\"Comparing the estimates in Julia and R\")\n",
    "printmat([b b_R[:,1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "RObject{RealSxp}\n",
       "           [,1]\n",
       "[1,] -0.5041626\n",
       "[2,]  1.3410486\n"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#we can run blocks of code like this\n",
    "#do @rput x y or use $x and $y in the code below\n",
    "\n",
    "#solve x'x*b = x'y\n",
    "R\"\"\"\n",
    "xx <- t(x)%*%x\n",
    "xy <- t(x)%*%y\n",
    "b_R <- solve(xx,xy)\n",
    "\"\"\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Interactive Usage of R\n",
    "\n",
    "If you are using the Julia REPL (\"command prompt\"), then you can work more interactively with R. Unfortunaterly, that does not work in a notebook. The following trivial example illustrates the idea (`>julia` means that you are at the julia prompt and `R>` at the R prompt).\n",
    "\n",
    "```\n",
    "julia> x = 1;y=2\n",
    "julia> @rput x y\n",
    "julia> $         #to switch over to R\n",
    "R> z <- x + y\n",
    "R> hit ESC       #to switch over to Julia\n",
    "julia> @rget z\n",
    "```\n",
    "\n",
    "At this point you can work with `z` in Julia."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# C\n",
    "\n",
    "This section illustrates some simple examples of how to call a C function. The functions are in the file `My_C_Stuff.c` which contains:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "#include <stddef.h>\r\n",
      "\r\n",
      "// calculate the inner (dot) product of vectors Y and Y, returns the result (Sxy)\r\n",
      "double c_dot(size_t n, double *Y, double *X) {\r\n",
      "    double Sxy = 0.0;\r\n",
      "    for (size_t i = 0; i < n; ++i) {\r\n",
      "        Sxy += X[i]*Y[i];\r\n",
      "    }\r\n",
      "    return Sxy;\r\n",
      "}\r\n",
      "\r\n",
      "// calculate a simple regression, Y = a + b*X + u, puts (a,b) in vector ab, returns nothing\r\n",
      "void c_ols(size_t n, double *Y, double *X, double *ab) {\r\n",
      "    double Sx = 0.0, Sy = 0.0, Sxx = 0.0, Sxy = 0.0;\r\n",
      "    for (size_t i = 0; i < n; ++i) {\r\n",
      "        Sx  += X[i];\r\n",
      "        Sy  += Y[i];\r\n",
      "        Sxx += X[i]*X[i];\r\n",
      "        Sxy += X[i]*Y[i];\r\n",
      "    }\r\n",
      "    ab[1] = (Sxy-Sx*Sy/n)/(Sxx-Sx*Sx/n);   //slope\r\n",
      "    ab[0] = (Sy - ab[1]*Sx)/n;             //intercept\r\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "println(read(\"Data/My_C_Stuff.c\",String))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To compile to a dynamlic library (dll on windows), I use gcc (for x86_64) from [mingw-64](http://mingw-w64.org)\n",
    "and run the following in the mingw terminal\n",
    "```\n",
    "gcc -shared -fPIC My_C_Stuff.c -o My_C_Stuff.dll\n",
    "```\n",
    "\n",
    "To call the C functions, place the dll file in the current folder and then run the following cells."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Comparing the estimates in Julia and C\n",
      "    -0.504    -0.504\n",
      "     1.341     1.341\n",
      "\n"
     ]
    }
   ],
   "source": [
    "mylibc = \"My_C_Stuff.dll\"\n",
    "\n",
    "b_c = zeros(2)          #where C will store the regression results\n",
    "x2  = x[:,2]\n",
    "@ccall mylibc.c_ols(length(y)::Csize_t, y::Ptr{Float64}, x2::Ptr{Float64}, b_c::Ptr{Float64})::Cvoid\n",
    "println(\"Comparing the estimates in Julia and C\")\n",
    "printmat([b b_c])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In this call `mylibc.c_ols` is the library.function, `length(y)::Csize_t` is the first input and its type (an `Int` indicating the number of elements in `y`), `y::Ptr{Float64}` is the second input and its type (an `Array{Float64}`)  and similarly for the remaining inputs. `Cvoid` is the type of the output, which here indicates that the function does not have an output. Rather, the function modifies the vector `b_c` by putting the OLS results there. \n",
    "\n",
    "We could potentially wrap this in a Julia function that checks for the right input types and outputs the `b_c`  vector.\n",
    "\n",
    "In the next example, we instead use a function that outputs a `Float64` number. The function calculates the inner product of two vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "The inner product of x2 and y in Julia and C:   11071.648           11071.648\n"
     ]
    }
   ],
   "source": [
    "z = @ccall mylibc.c_dot(length(y)::Csize_t, y::Ptr{Float64}, x2::Ptr{Float64})::Float64\n",
    "\n",
    "printlnPs(\"The inner product of x2 and y in Julia and C:  \",x2'y,\" \",z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "kernelspec": {
   "display_name": "Julia 1.6.1",
   "language": "julia",
   "name": "julia-1.6"
  },
  "language_info": {
   "file_extension": ".jl",
   "mimetype": "application/julia",
   "name": "julia",
   "version": "1.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 3
}
