{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Functions of Arrays\n",
    "\n",
    "This notebook illustrates how to apply a function to arrays. Another notebook focuses on matrix algebra."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Load Packages and Extra Functions"
   ]
  },
  {
   "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\n",
    "\n",
    "include(\"jlFiles/printmat.jl\")   #a function for prettier matrix printing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Elementwise Functions of Arrays: Using Dot (.)\n",
    "\n",
    "Let `X` be an array, and `a` and `b` be scalars. Then, `y = fn.(X,a,b)` generates an array `y` where `y[i,j] = fn(X[i,j],a,b)`\n",
    "\n",
    "(You could achieve the same thing with ```map(xi->fn(xi,a,b),x)```.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "fn (generic function with 1 method)"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "fn(x,a,b) = a/x + b             #x has to be a scalar for this to work"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   110.000    60.000\n",
      "       Inf    20.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "X = [1 2;\n",
    "     0 10]\n",
    "\n",
    "printmat(fn.(X,100,10))         #notice the dot."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Looping over Columns or Rows\n",
    "\n",
    "Suppose you want to calculate the sum of each row of a matrix. The classical way of doing that is to loop and extract each row as `X[i,:]`\n",
    "\n",
    "Notice that instead of `sum(X[:,i])` it may be faster and save memory to do `sum(view(X,i,:))` because this avoids creating a new array of `X[:,i]`.\n",
    "\n",
    "Also, there is a direct way to loop over all rows by using `for r in eachrow(X)`. To also get the row number `i`, we use `for (i,row) in enumerate(eachrow(X))`. For looping over columns, use `eachcol()`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "X:\n",
      "     1         2    \n",
      "     0        10    \n",
      "\n",
      "sum of each row:\n",
      "     3.000\n",
      "    10.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "X = [1 2;\n",
    "     0 10]\n",
    "println(\"X:\")\n",
    "printmat(X)\n",
    "\n",
    "m = size(X,1)\n",
    "\n",
    "z = zeros(m)                   #to fill with results, could here use zeros(Int,m)\n",
    "for i = 1:m                    #loop over rows\n",
    "    z[i] = sum(X[i,:])         #or sum(view(X,i,:)) to save memory\n",
    "end\n",
    "println(\"sum of each row:\")\n",
    "printmat(z)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sum of each row:\n",
      "     3.000\n",
      "    10.000\n",
      "\n"
     ]
    }
   ],
   "source": [
    "z = zeros(m)                   \n",
    "for (i,row) in enumerate(eachrow(X))   #enumerate to get both index and value\n",
    "    z[i] = sum(row)\n",
    "end\n",
    "println(\"sum of each row:\")\n",
    "printmat(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Functions with Built-in `dims` Argument\n",
    "\n",
    "May functions have a `dims` argument that allows you to avoid looping, for instance, `sum`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     3    \n",
      "    10    \n",
      "\n"
     ]
    }
   ],
   "source": [
    "printmat(sum(X,dims=2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Functions with Built-in `function` and `dims` Arguments (extra)\n",
    "\n",
    "Several functions allow you to also apply an elementwise function to `X` before doing the rest of the calculations, for instance, `any`, `all`, `sum`, `prod`, `maximum`, and `minimum`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2×1 Matrix{Int64}:\n",
       "   5\n",
       " 100"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sum(abs2,X,dims=2)           #same as sum(abs2.(X),dims=2) but faster"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Apply Your Own Function on Each Column: mapslices (extra)\n",
    "\n",
    "...or each row (or some other dimension)\n",
    "\n",
    "The `mapslices(fun,x,dims=1)` applies `fun(x[:,i])` to each column of a matrix `x`. This is an alternative to looping over the columns.\n",
    "\n",
    "The cell below illustrates this by calling a function which calculates the moving average of `x[t]` and `x[t-1]` for each column of a matrix `X`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "      X (with 2 columns) and Y (MA(1) of X):\n",
      "     1.000   101.000       NaN       NaN\n",
      "     2.000   102.000     1.500   101.500\n",
      "     3.000   103.000     2.500   102.500\n",
      "     4.000   104.000     3.500   103.500\n",
      "     5.000   105.000     4.500   104.500\n",
      "\n"
     ]
    }
   ],
   "source": [
    "function MovingAvg2(x)             #moving average of t and t-1\n",
    "    T = length(x)\n",
    "    y = fill(NaN,T)\n",
    "    for t = 2:T\n",
    "        y[t] = (x[t] + x[t-1])/2\n",
    "    end\n",
    "    return y\n",
    "end\n",
    "\n",
    "X = [1:5 101:105]\n",
    "Y = mapslices(MovingAvg2,X,dims=1)\n",
    "println(\"      X (with 2 columns) and Y (MA(1) of X):\")\n",
    "printmat([X Y])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "@webio": {
   "lastCommId": null,
   "lastKernelId": null
  },
  "anaconda-cloud": {},
  "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.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
