1 /*
  2  * Hodrick-Prescott-Filter
  3  * copyright: Sebastian Krug (2014) and Kurt Annen (2004)
  4  * Version:   1.0
  5  * 
  6  * This code is based on the Java version of the HP-filter written by Kurt Annen in 2004.
  7  * General usage (command line):
  8  * 1. go to the folder consisting of this "HPfilter.scala"-file and the file containing the data (for example "inputFile.csv")
  9  * 2. compile the scala code with: scalac HPfilter.scala
 10  * 3. now type use it with: scala hpFilter /l 14400 /s:c /i inputFile.csv /o outputFile.csv
 11  *    or
 12  *    provide data as argument directly via: scala hpFilter /l 14400 /s:c /s 1.3 2.1 3.7 4.7 5.3 /o outputFile.csv
 13  * 4. If you need further info, type: scala hpFilter /help
 14  * 
 15 */
 16 
 17 import java.io._
 18 import java.util._
 19 import collection.mutable._
 20 import scala.collection.JavaConversions._
 21 
 22 object hpFilter {
 23 
 24   var filterData = true
 25   var str        = ""
 26   var lambda     = 1600
 27   var inputFile  = ""
 28   var outputFile = ""
 29   var separator  = "\n"
 30 
 31 
 32 
 33   /** 
 34    *    This function reads the data to filter and applies the HP-filter on it.
 35    *    */
 36   def main (args: Array[String]) {
 37     val writeOutputToFile = if(args.contains("/o")) true else false
 38 
 39     args.length match {
 40         case 0 => printHelp
 41 
 42         case _ =>
 43           if(args.contains("/h") || args.contains("/help")) printHelp else {
 44             args.foreach{
 45               i =>
 46                 i match {
 47                   case "/i"   => inputFile     = args(args.indexOf(i) + 1)
 48                   case "/l"   => lambda    = args(args.indexOf(i) + 1).toInt
 49                   case "/s:f" => separator = " "
 50                   case "/s:n" => separator = "\n"
 51                   case "/s:c" => separator = ", "
 52                   case "/s:s" => separator = "; "
 53                   case "/s"   =>
 54                     var firstIndexOfInputStream    = args.indexOf(i) + 1
 55                     var firstIndexAfterInputStream = if(writeOutputToFile) args.length - 2 else args.length
 56                     outputFile = if(writeOutputToFile) args.last else ""
 57                     args.slice(firstIndexOfInputStream, firstIndexAfterInputStream).foreach(x => str += x + separator)
 58                   case "/o" => outputFile = args.last
 59                   case _    =>
 60                 }
 61             }
 62           }
 63     }
 64 
 65 
 66     val buffer = Array.ofDim[Byte](400000)
 67     if(filterData) {
 68       try {
 69         if(inputFile != "") {
 70           if(writeOutputToFile) println(s"HP-filtered data of $inputFile saved to $outputFile") else println(s"HP-filter data of $inputFile yields")
 71           var in   = new FileInputStream(inputFile)
 72           val len  = in.read(buffer, 0, 400000)
 73           val stri = new String(buffer, 0, len)
 74           str = stri
 75           in.close()
 76         }
 77         val datame = str.split(separator)
 78         val N      = datame.size
 79         var data   = datame.map(_.toDouble)
 80 
 81         val a = ArrayBuffer[Double]( 1 + lambda, 5 * lambda + 1) ++= Array.fill[Double](N-4)(6 * lambda + 1) ++= Array[Double](5 * lambda + 1, 1 + lambda)
 82         val b = ArrayBuffer[Double](-2 * lambda) ++= Array.fill[Double](N-3)(-4 * lambda) ++= Array[Double]( -2 * lambda, 0)
 83         val c = ArrayBuffer[Double]() ++= Array.fill[Double](N-2)(lambda) ++= Array[Double]( 0, 0)
 84         
 85         data = pentas(a, b, c, data, N)
 86 
 87         if(writeOutputToFile == false) println(data.mkString(separator)) else {
 88           val out = new FileWriter(outputFile)
 89           out.write(data.mkString(separator))
 90           out.close()          
 91         }
 92         
 93       } catch {
 94         case e: Exception => println(e.toString)
 95       }
 96     }
 97   }
 98 
 99 
100 
101   /** 
102    *    This function just prints some helping information.
103    *    */
104   def printHelp {
105     filterData = false
106     println(" \n")
107     println("Scala - Hodrick-Prescott-Filter (Version 1.0) \t [Copyright (c) Sebastian Krug (2014)]\n")
108     println("scala hp [/switch] [/input] [/output]")
109     println(" /i \t - reads data to filter from input file")
110     println(" /o \t - save filtered data in output file")
111     println(" /s \t - reads data to filter from Stream")
112     println(" /l \t - Lambda (standard is 1600) ")
113     println(" /h \t - prints help text")
114     println(" /s:x \t - separator (standard is new line) ")
115     println("\t   n - new lines")
116     println("\t   f - spaces")
117     println("\t   c - commas")
118     println("\t   s - Semi-colons\n")
119     println("Examples for usage:")
120     println("scala hpFilter /l 14400 /s:c /i data.csv /o hpdata.csv")
121     println("scala hpFilter /l 14400 /s:n /s 1.3 2.1 3.7 4.7 5.3 /o hpdata.txt\n")
122     println("Hint: Without the switch /o you can redirect the output with \">>\".")
123     println("For questions, mail me to krug84@gmail.com.")
124     println(" \n")
125   }
126 
127 
128 
129 
130   /** 
131    * This function solves the linear equation system BxX=Y with B being a pentadiagonal matrix.
132    */
133   def pentas (a:ArrayBuffer[Double], b:ArrayBuffer[Double], c:ArrayBuffer[Double], data:Array[Double], N:Int):Array[Double] = {
134     var H1  = 0.0
135     var H2  = 0.0
136     var H3  = 0.0
137     var H4  = 0.0
138     var H5  = 0.0
139     var HH1 = 0.0
140     var HH2 = 0.0
141     var HH3 = 0.0
142     var HH5 = 0.0
143     var Z   = 0.0
144     var HB  = 0.0
145     var HC  = 0.0
146 
147     for(x <- 0 until N){
148       Z    = a(x) - H4 * H1 - HH5 * HH2
149       HB   = b(x)
150       HH1  = H1
151       H1   = (HB - H4 * H2) / Z
152       b(x) = H1
153       HC   = c(x)
154       HH2  = H2
155       H2   = HC / Z
156       c(x) = H2
157       a(x) = (data(x) - HH3 * HH5 - H3 * H4) / Z
158       HH3  = H3
159       H3   = a(x)
160       H4   = HB - H5 * HH1
161       HH5  = H5
162       H5   = HC 
163     }
164     H2 = 0
165     H1 = a(N - 1)
166     data(N - 1) = H1
167     for(x <- N-2 to 0 by -1){
168       data(x) = a(x) - b(x) * H1 - c(x) * H2
169       H2 = H1
170       H1 = data(x)
171     }
172     data
173   }
174 
175 
176 }