Snippet: LinearLeastSquares en BASH (una animalada)

Hice la animalada esta en bash para mis alumnos, y la verdad que uno aprende asi cosas del lenguaje, entre ellas el comando “bc” que es un lenguaje en si mismo.

Uso:

user@host:~$ bash linearleastsquares.sh -x "1 2 3 4 5" -y "1 4 9 16 25" -i 1

Codigo:

#!/bin/bash

# "linearleastsquares.sh" a simple interpolator for bash.
# Copyright (C) 2011  - JBC <jbc dot develop at gmail dot com

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


#===============================================================================
# META
#===============================================================================

PRJ=$0
AUTHOR="JBC <jbc dot develop at gmail dot com>"
DATE="2011/01/13"
VERSION="0.1"
LICENSE="GPL3"
LICENSE_URL="<http://www.gnu.org/licenses/gpl.html>"

USAGE="
usage: $PRJ -x <args> -y <args> -i <args>n
   or: $PRJ -hn
   or: $PRJ -vn

t    -h t    This help. n
t    -v t    Version. n
t    -x <string> t    String with all x values (same lenght as y). n
t    -y <string> t    String with all y values (same lenght as x). n
t    -i <string> t    String with values to be intrpolated. n
n
'$PRJ'  Copyright (C) 2011  $AUTHOR n
This program comes with ABSOLUTELY NO WARRANTY; for details type see
$LICENSE_URL. n
This is free software, and you are welcome to redistribute it under certain
conditions; see $LICENSE_URL for details. n";

USE_HELP="Try «$PRJ -h» for more information.";


#===============================================================================
# ERRORS
#===============================================================================

ERROR_INVALID_ARGUMENT=( "Invalid argument." 1 );
ERROR_XY_NEQ_LENGTH=( "'-x' and '-i' aguments must have a same leght." 2 );
ERROR_NO_ARG_X=( "No arguments for '-x'" 3 );
ERROR_NO_ARG_Y=( "No arguments for '-y'" 4 );
ERROR_NO_ARG_I=( "No arguments for '-i'" 5 );


#===============================================================================
# GET OPTS
#===============================================================================

while getopts "hvx:y:i:" flag;
do
    case $flag in
        h)
            echo -e $USAGE;
            exit 0;;
        v)
            echo -e $VERSION;
            exit 0;;
        x)
            xs=$OPTARG;;
        y)
            ys=$OPTARG;;
        i)
            is=$OPTARG;;
        ?)
            echo -e "${ERROR_INVALID_ARGUMENT[0]}" ;
            echo -e $USE_HELP;
            exit "${ERROR_INVALID_ARGUMENT[1]}";;
    esac;
done;


#===============================================================================
# VALIDATE LENGTHS
#===============================================================================


if [ $(echo $xs | wc -w) -eq 0 ]; then
    echo -e "${ERROR_NO_ARG_X[0]}" ;
    echo -e $USE_HELP;
    exit "${ERROR_NO_ARG_X[1]}";
fi;


if [ $(echo $ys | wc -w) -eq 0 ]; then
    echo -e "${ERROR_NO_ARG_Y[0]}" ;
    echo -e $USE_HELP;
    exit "${ERROR_NO_ARG_Y[1]}";
fi;


if [ $(echo $is | wc -w) -eq 0 ]; then
    echo -e "${ERROR_NO_ARG_I[0]}" ;
    echo -e $USE_HELP;
    exit "${ERROR_NO_ARG_I[1]}";
fi;


if [ $(echo $xs | wc -w) -ne $(echo $ys | wc -w) ]; then
    echo -e "${ERROR_XY_NEQ_LENGTH[0]}" ;
    echo -e $USE_HELP;
    exit "${ERROR_XY_NEQ_LENGTH[1]}";
fi;


##===============================================================================
## CALCULATE TERMS FOR "Y = $m * X + $b"
##===============================================================================

i=1;

n=$(echo $xs | wc -w);
sum_xy=0;
sum_x=0;
sum_y=0;
sum_x_sq=0;

while [ $i -le $n ]; do
   xi=$(echo $xs | cut -d " " -f $i);
   yi=$(echo $ys | cut -d " " -f $i);
   sum_xy=$(echo "$sum_xy + ($xi * $yi)" | bc -l);
   sum_x=$(echo "$sum_x + $xi" | bc -l);
   sum_y=$(echo "$sum_y + $yi" | bc -l);
   sum_x_sq=$(echo "$sum_x_sq + $xi ^ 2" | bc -l);
   i=$(echo "$i + 1" | bc -l);
done

m=$(echo "($n * $sum_xy - $sum_x * $sum_y) / ($n * $sum_x_sq - $sum_x ^ 2)" | bc -l);
b=$(echo "($sum_y - $m * $sum_x) / $n" | bc -l);


#===============================================================================
# INTERPOLATE
#===============================================================================

for x in $is; do
    echo $(echo "$b + $m * $x" | bc );
done;

exit 0

Disclaimer: Solo lo probé con el conjunto de datos que hay en Wikipedia.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>