C'est l'une des rares fois ou je vais parler de mon taff ici, profitez-en :)
Lorsque l'on a affaire à des champs sous forme binaire exclusif les uns aux autres, il est parfois utile de les regrouper pour n'avoir qu'un attribut de differentes valeur à afficher. Par exemple, on peut vouloir transformer : 1000, 0010, 0010, 0100, 0001 en 1, 3, 3, 1, 4. L'espace des dimensions est ainsi réduit (ici on passe de 4 dimension à une seule). Générallement, en Data Mining, c'est plutôt l'inverse qui est réalisé. Mais lorsqu'on veut traiter des données déja classifier/discrétiser on peut se retrouver dans cette situation.
On voit donc qu'il suffit de définir la catégorie comme étant la position du bit à 1 dans les dimensions analysées. Avec un script shell, c'est rapidement réalisé.
#!/bin/sh
#-------------------------------------------------------------------------------
# author : Guillem LEFAIT
# Goal : transform exclusive binary attributes to one categorical attribute
# inside a data file as fast as possible
# credit : works done in ucd.ie
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# GLOBAL VARIABLE
OUT=0
COMPRESS_OUT=""
LINE_OUT=""
PROGRAMME_TMP="biniou.$$"
PROGRAMME_RES="$1.converted"
#-------------------------------------------------------------------------------
# what : check if binary attributes are really exclusives
# input :
# $1 = some binary values separated by $2
# $2 = separator
# $3 = start column
# $4 = end column
function exclusif_binary()
{
nb_match=`cat $1 | cut -d "$2" -f$3-$4 | tr -d "$2" | grep -e "^0*10*$" -c`
nb_lines=`cat $1 | wc -l`
if [ $nb_match -eq $nb_lines ]
then
OUT=0
else
OUT=1
fi
return $OUT
}
#-------------------------------------------------------------------------------
# what : converting an attribute to its category
# input :
# $1 = binary list to "compress"
# $2 = separator
function converting_one()
{
binary=`echo $1 | tr -d "$2"`
COMPRESS_OUT=`expr index "$binary" 1`
}
#-------------------------------------------------------------------------------
# what : compressing an instance with some (at least 1) intervall of binary
# attributes
# input :
# $1 = the line to process
# $2 = separator
# $2+i } ith interval (column starting)
# $2+i+1 } ith interval (column ending)
#
function converting_line()
{
line=$1
sep=$2
shift 2
res=""
last=1
min=`expr $1 - 1`
add=""
while [ $# -ge 2 ]
do
if [ $last -lt $1 ]
then
v=`echo $line | cut -d "$sep" -f$last-$min`
res="$res$add$v"
fi
add="$sep"
v=`echo $line | cut -d "$sep" -f$1-$2`
converting_one $v $sep
res="$res$add$COMPRESS_OUT"
min=$last
last=`expr $2 + 1`
shift 2
done
v=`echo $line | cut -d "$sep" -f$last-`
if [ "$v" != "" ]
then
res="$res$add$v"
fi
LINE_OUT="$res"
}
#-------------------------------------------------------------------------------
# what : processing a file
# input : $1 = file
# $2 = separator
# $3,$4, ...., $n-1, $n = intervall list
function process_all()
{
file_in="$1"
in=`cat $file_in`
shift
for line in $in
do
converting_line $line $*
echo "$LINE_OUT" >> "$PROGRAMME_TMP"
done
}
#-------------------------------------------------------------------------------
# checking and lauching the process
if [ $# -lt 4 ]
then
echo "usage : $0 file separator (colum_start column_end)+"
exit 1
fi
date
rm "$PROGRAMME_TMP"
process_all $*
cat "$PROGRAMME_TMP" | tr "," "\t" > "$PROGRAMME_RES"
date
# EOF
################################################################################
L'avantage de cette solution, par rapport à la première version est que le fichier préparé se construit au fur et à mesure. Dans la première version je transformais les intervalles "colonne" par "colonne".
Quel est donc mon "problème" ? Et bien mes solutions ne sont pas performantes. Je cherche à transformer des données : environ 600 000 instances, 55 attributs dont 2 intervalles d'attributs binaires à réduire en 2 dimensions (de taille 4 et 40 respectivement). Pour transformer 10 000 données, mon script shell met un peu moins de 5min, soit un traitement de 33 instances par seconde, soit plus de 5 heures pour transformer mon fichier.
Est-ce que quelqu'un a une/des solution(s)/idée(s) pour augmenter les performances d'un script de ce type ?