Snippet: LinearLeastSquares en BASH (una animalada)
2011/01/15 Deja un comentario
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 -h\n
or: $PRJ -v\n
\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.

