vendredi 21 octobre 2011

Tableaux de hashmap

Soit les hashmap suivantes:

my %h1 = ("toto"=>"toto12", "test"=>"test12");
my %h2 = ("xavier"=>"0471...", "vincent"=>"047945...");

Il n'est pas permis d'écrire ceci en Perl

my @array=undef;
@array[0]=%h1;
@array[1]=%h2;

Il est impératif de référencer les hashmap dans le tableau:

my @array=undef;
@array[0]=\%h1;
@array[1]=\%h2;

Ceci fonctionnera beaucoup mieux.

Là où cela devient amusant c'est pour récupérer les hashmap contenue dans le tableau:

$array[1]->{vincent} me donnera 047945...

si je veux defnir %hmap qui contiendra la hash contenue dans $array[1] je pourrais faire comme suit:
%hmap=%$array[1];
Cela fera malheureusement planter PERL lamentablement.
soit j'écris

$hmap_ref=$array[1];
%hmap=%$hmap_ref;

Cela marchera mais pour un développeur PERL cette écriture est beaucoup trop lourde:
On pourra donc faire plus concis (et plus illisible) en utilisant ceci:

%hmap=%{$array[1]}

Attention c'est la déréférenciation d'un tableau et non pas la valeur de la clé d'une hash (dont la clé serait $array[1]).

Voilà de quoi s'amuser. ..

lundi 17 octobre 2011

Hashmap en Perl

Voici la suite des manipulations de Hash en PERL

Tout d’abord une hash est une structure de données référencée par une clé généralement alphanumérique :
(Nom=clé) => Numéro de téléphone

Exemple :
Vincent => +3271546521

La clé sera ici vincent et la valeur associée, +32 blabla. En général on applique un hashage sur la clé (ou un digest) pour transformer la séquence de caractère en un integer plus ou moins unique par chaîne de caractère. Ceci a tendance à améliorer la rapidité des traitements.

Création d’une hash (hashmap)

my %h=( "jyce"=>"071456502",
"toto"=>"toto12");

La hash est un type particulier en PERL représenté par ‘%’
Toute variable déclarée avec un % est une hashmap
Pour obtenir le numéro de tel de jyce :

print $h{jyce} ; # attention pas %{}

Une fois crée il est toujours possible d’ajouter des nouveaux couples clé-valeur dans la hashmap.

# oups oublié, on rajoute directement comme des cochons dans la hashmap
$h{"marc"}="071726532";

Fusionner deux hashmap :

my %h2 = ("vincent"=>"04754455",
"xavier"=>"0479581515");
%h=(%h,%h2); # pas mal hein ?

Afficher l’entiereté d’une hashmap

print "$_=".$h{$_}."\n" foreach (keys(%h)) ;

keys(%h) est une fonction qui renvoit un tableau contenant les clés de la hash :
@k=keys(%h) ;
Par métaphore, $_ représente donc un élément du tableau @k.
Nous allons créer une procédure qui affiche la hashmap :

sub map_params {
my (%m)=@_;
print "Non triée\n";
print "$_=".$m{$_}."\n" foreach (keys(%m)) ;
}
#appel
map_params(%h)

Référencer une hashmap. Dans certains cas de figure il est impératif d’utiliser une référence sur la hashmap.
Créeonns une procédure attendant une hashmap en paramètres, cette procédure aura ainsi par exemple la faculté d’ajouter des choses dans la hashmap et de faire en sorte que ces choses soient toujours là après l’appel (passage par référence) :

sub map_params_ref {
my ($mref)=shift;
map_params(%$mref); # déférenciation et on appel la proc standard d’affichage
}
#appel
map_params_ref(\%h);

remarquez que $mref->{vincent} fonctionne aussi très bien, je n’ai pas besoin dans ce cas de déréférencer.
Afficher une hashmap triée :
Je trie le tableau des clés et j’affiche les valeurs en parcourant le tableau de clé trié :

print "Triée\n";
my @key_sorted=sort ( {$a cmp $b} keys(%m) );
print $_.'='.$m{$_}."\n" foreach (@key_sorted);
trier dans l’ordre descendant
my @key_sorted=sort ( {$b cmp $a} keys(%m) );

Voilà j’espère que ce petit tuto sur les manipulations de hash vous a amusé...

vendredi 14 octobre 2011

Sub en PERL

PERL est un langage non typé et non signé les fonctions peuvent recevoir tout et n’importe quoi comme paramètres, cette manière assez crade de travailler peut vite amener du code à faire n’importe quoi...

Voyons quelques exemples:

Passer des paramètres à une sub
Définissons une fonction qui somme deux paramètres a et b : façon propre de faire

sub sum {
my ($a,$b)=@_;
return $a+$b;
}

Bon l’explication est simple, les paramètres d’une sub sont un tableau référencé par @_
La notation ($var1, $var2, … , $varn) permet de récupérer les éléments d’un tableau dans l’ordre 1 ..n
Mais pourquoi faire simple quand on peut faire compliquer , PERL va offrir d’atures méchanismes d’extraction dans un tableau
Ex :façon longue
sub sum {
my $a=shift(@_);
my $b=shift(@_);
return $a+$b;
}
Shift permet de retirer des éléments d’un tableau, (le premier entré est le premier sortant FIFO)
Façon plus simple
sub sum {
my $a=shift;
my $b=shift;
return $a+$b;
}
Dans une sub le paramètre @_ est implicite shift s’appliquera donc d’office à lui si rien n’est précisé.
Façon LIFO
sub sum {
my $b=pop;
my $a=pop;
return $a+$b;
}

Attention dans une LIFO le dernier entré est le premier à sortir (pile d'assiettes)
Façon bête :
sub sum{
my $a=$_[0];
my $b=$_[1];
return $a+$b;
}
Remarqué qu’ici on utilise $_[i] puisqu’on référence un élément du tableau qui est scalaire.
Ici nous avons vu le passage par valeur, il n’est pas possible de modifier les paramètres, voyons maintenant le passage par adresse.

Si je passe des variables en paramètre à cette fonction, la valeur des paramètres est copiée dans la fonction.
Donc toute modification apportée aux variables ne sera pas répercutée en dehors de la fonction.
Considérons la fonction suivante :
sub sum_ref {
my ($aref,$bref)=@_;
$$aref=3; # je remplace le paramètre entré 5 par 3.
my $a=$$aref; # je déférence dans $a
my $b=$$bref; # je déférence dans $b
return $a+$b;
}
my $x=5;
my $y=6;
print sum_ref(\$x,\$y);
print "\n$x";
Vous l’aurez compris la sortie va me donner ceci :
9
3
$x vallait cinq mais c’est son adresse qui a été passée. Donc c’est la valeurs contenue à cette adresse logique qui a été remplacée par 3.
Remarquez le bel opérateur de référencement \$var et l’opérateur de déréférencement $$var qui donne au coding une élégance digne des soirées de Monsieur l’Ambassadeur.

En conclusion je rapellerai cette magnifique citation de Dave Small :
Un langage de programmation est censé être une façon conventionnelle de donner des ordres à un ordinateur. Il n'est pas censé être obscur, bizarre et plein de pièges subtils ça, ce sont des attributs de la magie.

Il devait certainement penser à PERL lorsqu’il a écrit cela.

vendredi 30 septembre 2011

La classe box-plot

Pour la création du contenu de la classe du box-plot nous allons nous appuyer sur un framework javascript relativement bien pensé pour tout ce qui touche au graphisme, il s’agit de « raphaeljs ».
Ce petit framework permet de créer un objet de type Raphael, cet objet fournit un canvas sur un panel div contenu dans le browser.
L'utilité d'un framework n'est plus à démontrer puisqu'il permet:
- de ne pas ré-inventer la roue
- de déléguer à d'autres personnes plus expertes des tâches secondaires pour notre projet
- de se focaliser sur notre propre business
- de profiter des évolutions et des améliorations du framework sans devoir mettre les mains dans le camboui.
Ce canvas supporte différente méthode graphiques donc la méthode « path » basée sur une notation SVG (Scale Vector Graphics).
Path prend comme une paramètre une string constituée des attributs suivant : M x y, ce qui signifie MoveTo (x,y) en pixel et L x y, ce qui signifie LineTo.
Une autre méthode très utile est la méthode « text ».
X – coordonnée x en pixels
Y – coordonnée y en pixels
Text – une string contenant le texte à afficher.
Sur base de ces méthodes il est donc très aisé de construire notre classe boxplot .

Le framework raphaeljs peut-être télécharger ici

Et voici le code du boxplot:

function BoxPlot ( min, q1,q2,q3, max, mean, divTarget) {
this._divTarget=null;
this._name=name;
this._min=min;
this._q1=q1;
this._q2=q2;
this._q3=q3;
this._max=max;
this._mean=mean;

if (divTarget) this._divTarget=$(divTarget);

this.show=function () {

var sWidth=this._divTarget.style.width;
var sHeight=this._divTarget.style.height;
var iMaxX=sWidth.substring(0,sWidth.length-2); //this._divTarget.style.width.substring(1,;
var iMaxY=sHeight.substring(0,sHeight.length-2); //this._divTarget.style.height;
var q1_y=iMaxY*this._q1/this._max;
var q2_y=iMaxY*this._q2/this._max;
var q3_y=iMaxY*this._q3/this._max;
var mean_y=iMaxY*this._mean/this._max;

var canvas= Raphael(this._divTarget, sWidth, sHeight);
// top whisker
var ibx=0.20*iMaxX;
var iex=0.80*iMaxX;
canvas.path("M"+ibx+" "+iMaxY+" L "+iex+" "+iMaxY);
canvas.path("M"+ibx+" "+(iMaxY-1)+" L "+iex+" "+(iMaxY-1));
var imx=0.50*iMaxX;
// Box Plot
canvas.path("M"+imx+" "+iMaxY+" L "+imx+" "+(iMaxY-q1_y));
canvas.path("M 0 "+ (iMaxY-q1_y)+ " L "+iMaxX+" "+(iMaxY-q1_y));
canvas.path("M 0 "+ (iMaxY-q1_y)+ " L 0 "+(iMaxY-q2_y) );
canvas.path("M "+iMaxX+" "+ (iMaxY-q1_y)+ " L "+iMaxX+" "+(iMaxY-q2_y) );
canvas.path("M 0 "+ (iMaxY-q2_y) + " L "+iMaxX+" "+(iMaxY-q2_y) );
canvas.path("M 0 "+(iMaxY-q2_y)+" L 0 "+(iMaxY-q3_y) );
canvas.path("M "+iMaxX+" "+(iMaxY-q2_y) +" L "+iMaxX+" "+(iMaxY-q3_y));
canvas.path("M 0 "+(iMaxY-q3_y)+" L "+iMaxX+" "+(iMaxY-q3_y));
canvas.path("M "+imx+" "+(iMaxY-q3_y)+" L "+imx+" 0");
canvas.path("M "+ibx+ " 0"+" L "+iex+" 0");
canvas.path("M "+ibx+ " 1"+" L "+iex+" 1");
// Legend
canvas.text(16,4, ""+this._max);
canvas.text(16,(iMaxY-q3_y)+4, ""+this._q3);
canvas.text(16,(iMaxY-q2_y)+4, ""+this._q2);
canvas.text(16,(iMaxY-q1_y)-5, ""+this._q1 );
canvas.text(16,iMaxY-8, ""+this._min);
canvas.text(imx, (iMaxY-mean_y), "X");

}
}

lundi 26 septembre 2011

Javascript orienté objet


Contrairement aux apparences, Javascript permet de supporter des classes et des objets et par extension des frameworks complets peuvent-être écrits en Javascript (ex : prototypes, scriptacoulous, Ext Js, Raphaeljs…)

Je vais ici détailler une méthode classique de génération de classe en Javascript et donner une application directe à une classe permettant de générer des box-plots, graphique bien connu des statisticiens.

La création d’une classe en Javascript se fait simplement par la déclaration d’une fonction. Cette fonction utilise l’indentificateur « this ». Par défaut « this » pointe sur la fenêtre courante dans lequel le Javascript s’exécute. Toutefois si l’opérateur new est utilisé pour créer une instance de classe, this pointera sur cette instance plutôt que sur l’objet « window ».

Cet éclaricissement fait nous pouvons commencer à créer notre première classe :

function Boxplot ( min, q1,q2,q3, max, mean, divTarget) {

}

Vous me direz, ça ne ressemble pas vraiment à une classe mais à une bonn vieille function clasique de Javascript qui prend ,le minum, le quartile 25, la médiane, le quartile 75, le max et la moyenne de la distribution comme paramètre.

L’utilité du paramètre divTarget, très utile sera détaillé ultérieurement.

Ajoutons des membres et des méthodes à cette classe, pour se faire nous allons utiliser l’identificateur « this »

function Boxplot ( min, q1,q2,q3, max, mean, divTarget) {
this._min=min;
this._q1=q1;
this._q2=q2;
this._q3=q3;

...

}

Je vais créer autant de membres qu’il y a de paramètre dans mon constructeur et oui ici comme la classe est une fonction, son constructeur est d’office la fonction : Boxplot dans notre cas de figure.

J’utilise la notation _membre pour signifier que ce membre est privé, contrairement aux langages réellement orientés objet, Javascript ne dispose pas de restriction de visibilité. On prend donc cette convention venant de PERL pour signifier les éléments privés.

Il est intéressant d’avoir conçu une classe Boxplot toutefois si on ne peut pas l’afficher elle n’est pas très utile, nous allons donc ajouter une méthode à notre classe : show() qui permettra d’afficher le box-plot.

function Boxplot ( min, q1,q2,q3, max, mean, divTarget) {
this._min=min;
this._q1=q1;
this._q2=q2;
this._q3=q3;

...
this.show=function () {
… code de la fonction
}

}

Ici j’ai déclaré une fonction publique je n’indique pas d’underscore devant.

Concrètement comment cela va-t-il fonctionner ?

Je devrais réserver un emplacement où placer mon box-plot dans ma page web, typiquement, je vais définir un panel de type DIV qui contiendra mon résultat.

Ensuite je créer une instance de ma classe en lui passant les paramètres de la distribution que je veux boxplotter.

Concrètement ça nous donne ceci :

var boxplot = new BoxPlot(1,8.75,13,16.35,28,13.3,"test");
boxplot.show();

Test référant le panel DIV. Le résultat est donné en haut de page.

Dans le prochain billet je donnerai le détail de l’affichage du boxplot (le contenu dans la fonction show() ).

mercredi 21 septembre 2011

Calcul de taille d'échantillon

Lorsque l'on tente de tirer des conclusions sur une population, il est souvent commode de prendre un échantillon de cette population pour simplifier les prises de mesure. Toutefois cet échantillon doit-être représentatif de la population et aussi sufisamment grand que pour donner une confiance sufisante dans les résultats mesurer. Il existe des méthodes formelles de calcul de taille d'échantillon sur lesquelles je reviendrai ultérieurement (Seuil de signification et puissance du test). Mais très souvent, on retrouve la formule suivante:

n=sqrt(N)+1

n= taille del'échantillon
N= taille de la population, cela implique que la taille de la population doit être connue, ce qui n'est pas toujours le cas!

Il faut savoir que cette formule est totalement empirique, inutile de trouver une démonstration mathématique permettant d'arriver à ce résultat, mais que malgré les mise en garde, elle est encore tès souvent utilisée.

L'étude suivante, illustre que si N>30 et que la distribution est normale, dans 90% des cas, la formule de la racine carrée +1 donnera une taille d'échantillon capable de donner un intervalle de 95% autours de la moyenne...

mercredi 3 août 2011

Bases d'Hibernate

Présentation
Hibernate est une implémentation de l'approche ORM (Object Relational Mapping) qui permet de persister des objets dans un storage (typiquement une base de données).
Le gros avantage de la technologie Hibernate est qu'outre le fait qu'elle soit compatible .net et Java, elle est aussi DB agnostique. L'utilisation du langage de query JPQL (Java Persistence Query Language) permet une traduction dans la plupart des SQL connus et supportés par Hibernate.
L'autre avantage est l'utilisation d'annotations dans le code source, qui permettent d'historiser en une fois le code et le mapping DB correspondant.
Bon une telle approche générique présente des avantages mais aussi des inconvénients:
- on ne sait pas toujours comment Hibernate travaille derrière notre dos, il n'est pas rare de voir des quantités phénoménales d'information retirée par un simple manque de connaissance des options.
- on est toujours limité au plus petit dénominateur commun des vendeurs DB. JPQL ne connaît pas toutes les astuces de chaque RDMS et quand bien même une telle approche nous ramènerait à devoir connaître le RDMS en question d'où pourquoi passer par une couche intermédiaire...

Annotations pour mapping hibernate:
Les annotations se placent toujours sur les getters Java et évitent de devoir utiliser des fichiers XML.

One-To-One
Dans l'entité ConditionDef
@OneToOne(mappedBy = "conditionDef")
sur getItemRef par exemple

Dans l'entité ItemRef:
@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "COLLECTEXCEPTCONDID", insertable = false, updatable = false)

La colonne apparaîtra dans l'entité ItemRef cela signifie que l'entité ItemRef est le maître. Si ItemRef est deleté alors ConditionDef sera delete automatiquement, par contre pour deleter ConditionDef il faudra mettre CollectEXCEPTCONDID à null dans la table ItemRef.

One-To-Many
Association classique de 1 vers plusieurs:
La join column sera crée dans l'entité pointée par le targetentity

@OneToMany ((targetEntity = Treatment.class, cascade = {
CascadeType.ALL
})
@JoinColumn(name = "RANDOLISTID")

Many-to-One
Une relation One-To-Many dans une entité doit comprendre une annotation Many-to-One dans l'autre qui référence la même colonne (afin d'éviter qu'hibernate se plante lamentablement)
@ManyToOne
@JoinColumn(name="RANDOLISTID")

Il existe aussi Many-to-Many mais je prefère ne pas en parler !

Créer de nouvelles classes persistentes

A) Modifier Object Facotry (ajouter les creators dans le factory pour récupérer les éléments JAXB)

B) Persistence.xml: ajouter les classes

C) ApplicationContext-orm.xml
ajouter un bean qui construit les daos de ces objets.

D) Module Service réaliser un autowire sur les daos crées.

dimanche 22 mai 2011

Etapes de creation d'une étude statistique

I) Formuler la question de recherche
Ex: montrer que les hommes sont plus grands que les femmes.

II) Transformer la question en une formulation statistique (quantitative)
La question doit se spécifier : 1 Outcome, 1 cible
A partir de là on va formuler le test d'hypothèse.

III) Définir H0 et H1
H1 étant ce que l'on veut prouver et H0 ce que l'on veut rejeter.
Ex: Je veux prouver que les hommes dépassent les femmes de 10cm
H0: mu(homme)-mu(femme)<=0
La puissance est maximale à l'égalité.
Toutefois je peux avoir un biais selon l'échantillonnage. Si j'ai prix des femmes suédoises et des hommes vietnamien, mon test est foireux car biaisé et pas représentatif de la population.

IV) Choix du design et définition du risque
Choisir un design, parallèle, cross over, ...

Risque que l'on est prêt à prendre sur l'erreur Type I (erreur alpha, ex:5%) : impact du hazard.
Ensuite on fixe l'erreur Type II (bêta): Ex 10% (probabilité d'atteindre la signification sur un écart donné).
La puissance du test est donnée par 1-bêta
Sur une taille d'échantillon fixe, plus l'écart entre H1 est petit plus la puissance sera faible. Cfr la fonction de puissance.

V) Collecte des données
On va collecter des données sur un échantillon dit EAS (Echantillonnage Aléatoire Simple) pour obtenir une p-value.
p-value: Probabilité que le hasard des tirages des unités statistiques produise un écart aussi important que celui observé dans les données.
A partir des données collectées on va construire une statistique de test, grâce à laquelle on va déterminer la p-value.

VI) Décision
On confronte la p-value à la borne que l'on s'est fixé et le chercheur décide de rejeter ou de garder l'hypothèse H0.
La décision n'appartient pas au statisticien mais au chercheur ou à l'expert du domaine concerné.

vendredi 29 avril 2011

Petit rappel sur le design des bases de données

Dépendance fonctionnelle

Notation: A->B

Si a tout instant, chaque valeur de A n'a qu'une valeur associée de B.

(Patient, Visite)->(Température, pression systolique, poids)

(Clé Primaire)->(Patient, Visite, Température, pression systolique, poids)

A la clé primaire, à tout instant, il n'y a qu'une valeur associée de tous les autres attributs de la table.

Propriété

Transitivité : Si A->B et B->C alors A->C


Formes Normales dans les bases de données.

But : Eliminer les redondances inutiles d'informations.

1NF : Première Forme Normale

a) les attributs de la relations contiennent une valeur atomique.

Ex: On crée la table tblVS

PatID VisitID VSTP VSDBP VSSBP VSWGHT

1 23 37.2 60 120 80

Autre exemple: supposons qu'il faille prendre différents points de température durant la visite pour le patient. Une façon rapide de résoudre le problème serait de séparer les prises de température par un “;”.

PatID VisitID VSTP VSDBP VSSBP VSWGHT

1 23 37.2;36.9;37.1 60 120 80

L'information est stockée toutefois la 1NF est violée car l'information contenue dans VSTP n'est pas atomique.

Une autre manière de faire serait de prévoir 3 champs de température supplémentaires.

PatID VisitID VSTP1 VSTP2 VSTP3 VSDBP VSSBP VSWGHT

1 23 37.2 36.9 37.1 60 120 80

A présent l'information est atomique, toutefois si on ne connaît pas le nombre de prises de température durant la visite, cela pose problème puisqu'il faut considérer le cas maximum. D'où gaspillage de place inutile.


b) Mettre en évidence les groupes répétitifs hors d'une table

Ici les groupes répétitifs sont: VSTP1, VSTP2, VSTP3

On peut donc les factoriser dans une autre table

PatID VisitID VSDBP VSSBP VSWGHT

1 23 60 120 80

Et là on crée une seconde table tblPriseTP

PatID VisitID VSTP

1 23 37.2

1 23 36.9

1 23 37.1


Ici on a factorisé l'information dynamique dans une autre tabl,e l'avantage est double:

  1. on évite d'utiliser des champs inutiles qui seront vides

  2. on gère dynamiquement les prises de températures sans devoir se fixer une borne maximale.

Remarque: cette factorisation peut se faire de manière récursive (c-à-d que la table factorisée peut peut-être elle même factorisée à nouveau, jusqu'à ce que ce ne soit plus possible).

  1. Les variables doivent être constante dans le temps.

Exemple plutôt que de stocker l'age du patient qui sera out of date un an plus tard, on conseille d'utiliser la birthdate et déduire l'age en utilisant: AGE=NOW – BIRTHDATE (var déduite)


2NF : Seconde forme normale

Postulat: La table concernée à déjà été normalisée par la 1NF (c'est un peu comme dans les trois lois de la robotique d'Isaac Asimov, les lois dépendent toujours des précédentes)

Les attributs non clé primaire dépendent de la totalité de la clé primaire. Tout attribut qui ne dépendrait que d'une partie de la clé primaire doit être factorisé de la table.

Exemple DM:

Soit la table tblVS contenant une information complémentaire : VSDT

PatID VisitID VSDT VSDBP VSSBP VSWGHT

1 23 18/05/2009 60 120 80

2 23 18/05/2009 75 118 65

1 24 19/05/2009 72 121 72

tblPriseTP

PatID VisitID VSTP

1 23 37.2

1 23 36.9

1 23 37.1

2 23 36.8

La normalisation à 1NF est totale sur une clé contenant deux champs PatID et VisitID

Toutefois, on remarque que VSDT ne dépend pas entièrement de PatID et VisitID mais uniquement de VisitID soit d'une partie de la clé primaire. Dans ce cas de figure je peux extraire VSDT et le placer dans une table pour économiser un champ (dans certains il se peut que plusieurs champs dépendent d'une partie de la clé primaire, donc l'économie est plus grande)

La 2NF me dit que je peux construire une troisième table qui contiendra VSDT

tblVS

PatID VisitID VSDBP VSSBP VSWGHT

1 23 60 120 80

2 23 75 118 65

1 24 72 121 72


tblVisit

VisitID VSDT

23 18/05/2009

23 19/05/2009


tblPriseTP

PatID VisitID VSTP

1 23 37.2

1 23 36.9

1 23 37.1

2 23 36.8


3NF – Troisième Forme Normale

Pour appliquer la troisième forme normale il faut être sure que la table répond déjà à la 2NF

Regrouper dans une autre table les attributs dépendants transitivement de la clé

Reprenons l'exemple 2NF avec les champs suivants dans la table principale: SiteID, City, Country


tblVS

PatID VisitID SiteID City Country VSDBP VSSBP VSWGHT

1 23 25 Arles France 60 120 80

2 23 26 Namur Belgique 75 118 65

1 24 25 Arles France 72 121 72

Ici on remarque une redondance entre City et Country

SiteID ne fait pas partie de la clé primaire, toutefois il y a une relation (PatID, VisitID)->SiteID

or a SiteID->(City,Country). Il y a donc transitivité (PatID,VisitID)->(City,Country). La 3NF peut donc s'appliquer on va créer un table tblSite qui reprendra l'information du Site


tblSite

SiteID City Country

25 Arles France

26 Namur Belgique


tblVS deviendra

PatID VisitID SiteID VSDBP VSSBP VSWGHT

1 23 25 60 120 80

2 23 26 75 118 65

1 24 25 72 121 72


Avantage :

  • supprime la redondance d'information

  • Dans l'ancienne version, supprimer le patient 2 avait pour risque de supprimer aussi le site de Namur de l'étude, ce n'est plus le cas après normalisation.


samedi 26 mars 2011

Imprimer sous Linux

Quelquefois Linux Lenny est capricieux avec les imprimantes USB. Une astuce simple pour reconfigurer votre imprimante est d'utiliser printconf

Si ce n'est déjà fait, en root:
#apt-get install printconf
Puis,
#printconf

Votre imprimante devrait être reconfigurée.

lundi 7 mars 2011

Skype (2.0) sous Debian Lenny

Comme vous l'aurez remarqué, Skype n'apparaît pas directement dans les Applications standard à installer sous Gnome. Tout simplement parceque Skype utilise les librairie graphiques QT (généralement distribuée avec KDE et qui en plus n'est pas totalement libre).

Pour s'installer skype nécessite les packages suivants:
  • libasound2
  • libc6
  • libgcc1
  • libqt4-core
  • libqt4-gui
  • libsigc++-2.0-0c2a
  • libstdc++6
  • libx11-6
La plupart des packages sont déjà installés sous Gnome, à l'exception des packages affichés en gras. Pour petit rappel:
#dpkg -l libqt4-core
vous indiquera si le package est installé ou pas. Vous pouvez le faire pour tous les packages.
Une fois les packages manquant déterminés, faire un apt-get install des packages manquants:
Ex:
#apt-get install libqt4-core libqt4-gui

Ensuite se rendre sur le site de skype:
http://skype.com/download/skype/linux/choose/
Choisir le package skype lenny et le télécharger dans /home/USER
Une fois l'opération effectuée:
#dpkg -i /home/USER/skype-debian_2.1.0.81-1_i386.deb
Et magie, Skype devrait s'installer:
Aller dans Applications/Internet/Skype pour pouvoir l'utiliser.

Actuellement la version de skype est malheureusement une version 2.0 (Beta) mais elle reste fonctionnelle malgré tout.

Pour supprimer skype
#dpkg -r skype

vendredi 25 février 2011

Le TIOBE-PCI

Le TIOBE Programming Community Index est un indicateur de popularité des langages de programmation. Cet index est basé sur le nombre de "hit" effectué sur différents moteurs de recherche concernant des noms de langages de programmation répértorié dans Wikipedia.

Cet index n'indique pas quel est le langage de le plus performant, ou celui qui détient le plus grand nombre de lignes de codes. Mais il s'agit d'un bon indicateur (remis à jour tous les mois) pour suivre les tendances ou les modes dans le domaine des langage de programmation.


Ici on remarque que pour 2011, Java et C tiennent le haut du sommet bien que leur utilisation semble décroître, par contre C# et Python semble prendre de l'importance. L'avenir nous le dira...

Le site internet du TIOBE contient toute une série de statistiques utiles qui peuvent vous aider dans la prise de décision de choix technologique. Maintenant, il est clair qu'une décision s'évalue aussi sur un tas d'autres paramètres que le top rank dans le TIOBE index ;-)

Source:TIOBE

mardi 22 février 2011

Les dix commandements de Steve Job

1er Commandement – Mettez-vous à l’écoute du monde
Pourquoi ? Parce que Steve Jobs explique qu’il a souvent trouvé ses idées en dehors de la profession, parfois avec des gens ordinaires. Il explique par exemple que lorsqu’en 1980 ils étaient en train de dessiner le nouveau Mac, il a aperçu un appareil ménager dans une vitrine et que séduit par son look il a dit à ses équipes que le prochain Mac devait y ressembler.

2ème Commandement – Limitez le nombre d’objectifs poursuivis
Peu de lancements, mais réussis. Une culture à l’opposé par exemple de Google qui lance tous azimuts, qui teste les réactions et accepte d’abandonner parce que c’est vraiment la politique de tester dans tous les sens.

3ème Commandement – Laissez tomber les dogmes
Et là, il l’illustre logiquement par un dogme, l’Open-Source … Pour lui ce qui importe c’est que le client prenne plaisir et intérêt à utiliser ses produits.

4ème Commandement – Sachez dire non aux perfectionnistes
Il explique qu’il joue le rôle de filtre, c'est à dire passer son temps à rappeler ses ingénieurs à l’ordre pour qu’ils fassent simple, pour éviter toutes ces fonctionnalités, ces touches, que l’utilisateur n’utilisera jamais selon lui. Il explique que les perfectionnistes cherchent à fabriquer un produit parfait … mais qui sera livré hors délais, et surtout hors de prix.

5ème Commandement – Dotez-vous d’un service après-vente irréprochable
Rien à dire, c'est logique.

6ème Commandement – Créez un mythe autour de vos produits
Il explique que les gens doivent acheter plus qu’un produit, ils doivent acheter du rêve, de la beauté, de la jeunesse !

7ème Commandement – Oubliez tout ce qui s’est fait par le passé
Exactement ! Par contre, il vous recommande de ne pas oublier de rêver. D’imaginer des produits, des développements incroyables, infaisables aujourd’hui. C’est ce qui vous donnera une avance sur vos concurrents.

8ème Commandement – Inspirez-vous des études … jusqu’à un certain point
Il parle ici des études clients, des focus groupes et autres. Il explique notamment qu’un client ne vous dira jamais quelle innovation il attend ! Par contre, ce même client va dire pourquoi il trouve vos produits et ceux de la concurrence géniaux ou nuls.

9ème Commandement - N’inventez pas, contentez-vous de perfectionner
Effectivement, Apple a rarement inventé des produits, comme Google d’ailleurs. Mais les deux entreprises sont attentives, ont le chic pour attirer des inventeurs ou pour racheter des innovations qu’elles intègrent dans leurs propres produits. En fait, la nouveauté pour la nouveauté, Steve Jobs trouve cela à la fois cher et dangereux.

10ème et dernier Commandement – Avancez à votre rythme, pas à celui des autres
Et imaginez juste que c’est un leader du marché qui dit qu’il faut avancer à son rythme. Il faut donc avancer, surveiller ce que fait la concurrence, mais surtout pas vous synchroniser sur une autre entreprise au péril de votre propre entreprise.

A méditer si vous voulez développer votre propre activité!
Source:http://www.lalibre.be/economie/actualite/article/643442/les-10-commandements-de-steve-jobs.html

jeudi 17 février 2011

Surrogate key in database design

L’usage de terme "surrogate" en informatique est parfois controversé.

Lorsque l’on conçoit une base de donnée, une des premières règles (imposées par les formes normales de conception) est de définir une clé primaire. Il s’agit d’un identifiant unique représentant un enregistrement donné et assurant qu’il ne peut être confondu avec une autre.
Les problèmes surviennent lors du choix de cette clé. Si je crée une table qui stocke les employés d’une entreprise, prendre le nom (ou le nom et le prénom) des personnes ne m’assurera pas forcémment l’unicité. Deux employés peuvent avoir les même noms et prénoms. D’où problème pour les reconnaître.
D’une manière général on introduit une colonne supplémentaire qui prend des valeurs entières uniques et qui identifie de manière distincte chaque enregistrement
ID Nom Prénom Age
1 Picard Jacques 30
2 Picard Jacques 24

En général les informaticiens appellent cette clé la clé primaire qui est par définition unique pour chaque enregistrement. (En pratique ça peut vite devenir différent). D'autres plus puristes diront qu'il s'agit d'une surrogate primary key (mais c'est plus rare).
En effet, une manière idiote de générer cette clé est qu’a chaque fois que j’enregistre un nouvel employé, je prends le maximum de la colonne ID et j’ajoute 1. “INSERT INTO tblEmployee (ID, Nom, PRENOM) VALUES (SELECT MAX(ID)+1, $Nom, $Prenom)”

Cela marchera bien si je suis l’unique utilisateur de la table employé, le problème est que le serveur de base de donnée sert plusieurs connections (utilisateurs connectés) en même temps. Que ce passe-t-il si deux personnes veulent faire une insertion dans la table employé au même moment.
U1 : veut insérer Alain Térieur qui a 36 ans. U1 Récupère le MAX(ID)+1=3 (ok)
U2 : veut insérer Jacques Cepte qui 28 ans. U2 Récupère le MAX(ID)+1 avant que U1 n’est eu le temps de faire l’insert=> Il obtient 3 aussi
U1 : fait son update et tout se passe bien
ID Nom Prénom Age
2 Picard Jacques 24
3 Térieur Alain 36
U2 : fait son update et il tente de mettre un second 3 dans le champ ID et là deux cas de figure, soit on a imposé une contrainte d’unicité sur le champ ID et heureusement la transaction explose, soit on ne l’a pas fait et on se retrouve avec clé primaire qui n’est plus unique (même si elle est censée l’être).
ID Nom Prénom Age
2 Picard Jacques 24
3 Térieur Alain 36
3 Cepte Jacques 28

Pour pallier à ce problème le vendeurs de bases de données ont crée des champs de type SERIAL ou AUTOINCREMENT, ce sont en général des entiers 32 bits. C'est-à-dire que ce n’est plus au programme de donner le numéro d’ID mais c’est le serveur de base de donnée qui s’en occupe. Ce type de clé primaire basée sur un tel champ est en général appelé par les informaticiens une surrogate key.
Avec une telle clé on se fiche de l’ID et pour insérer un utilisateur on indiquera simplement : INSERT INTO tblEmployee (Nom, Prenom) VALUES (‘Térieur’, ‘Alain’)
L’ID sera automatiquement incrémenté par la base de donnée dans ce cas de figure ma clé primaire sera toujours unique d’où le qualificatif de surrogate. Ceci est encore malheureusement de la théorie.
En pratique certains systèmes peuvent se connecter à une base de donnée y récupérer des données et puis s’en déconnecter. (Exemple : des moniteurs qui vont visiter un site clinique où il n’ont pas un accès directe à la base de donnée de la CRO, ils récupèrent les données sur leur PC, ajoute des nouvelles données lors de la visite, puis ils vont resyncrhoniser à leur retour avec la base de donnée d’origine). Même avec des clés primaires de type Autoincrement, l’utilisateur A aura de bonne chance d’avoir les mêmes valeurs de clé dans la base de donnée locale de son système que l’utilisateur B qui visitait le même site que lui au même moment.
Le système embarquer devra soit faire la réconciliation lui-même (ce qui est en pratique une très mauvaise idée)
Soit le système de base de donnée est sufisamment malin que pour réconcilier (ce n’est pas toujours gagné)
Soit on utilise un autre type de champ pour la clé primaire, pas mal de serveur de base de données aujourd’hui proposent des types GUID (Global Unique Identifier). Sa taille est de 16 octets (nettement plus lourd qu’un type integer) mais chaque valeurs générées est vraiment unique =>P(générer une valeur valeur existe)=0. Certains appelleront ce type de clé des surrogate keys alors qu'ils appelleront les autres type de champ clé des simple primary keys... Le principe consiste à définir clairement les conventions au sein d'une équipe.

mercredi 16 février 2011

Importer des données dans SAS

SAS Dataset et SAS DataStep

Chaque DATASET (tableau de données dans SAS) contient une partie métadonnée (PROC CONTENT) et une partie donnée (PROC PRINT). Le remplissage d'un DATASET se fait suivant différentes phases.

Phase de compilation :
Vérification de la syntaxe + Allocation mémoire définie par les métadonnées => On construit un dataset vide, si pas de place SAS s’arrête.
Le RUN indique à SAS que la phase de compile est terminée et qu’il peut exécuter

Phase d’exécution
Chaque ligne de la source des données est lue est ajoutée au dataset.
La variable _N_ est incrémentée, toutes les autre variables sont réinitialisée sauf si RETAIN.
Le contenu de input est mis dans un buffer puis copier dans le Program Data Vector (définit par les métadonnée). Une fois ceci fait on passe au datastep suivant. Il y a un datastep effectué par donnée à placer dans le dataset.

Importation
La première chose à faire lorsque l'on veut importer des données en SAS est en général de créer une bibliothèque (librairie) d'importation.

LIBNAME MALIB "c:\test"

Cette instruction est en général l'entête du fichier de code SAS. Le premier argument est le nom de la librairie, le second est la localisation physique sur le disque où les DATASET seront sauvés.

Cas simple:

Nous allons importer un simple fichier texte. Il faut savoir que par défaut SAS utilise l'espace comme séparateur de donnée et le CRLF comme séparateur d'enregistrement.

Voici donc le fichier testds.txt:


Nous avons deux champs et 3 enregistrement. Nous nommeront les champs VAL1 et VAL2. Le code pour importer ce fichier est le suivant:

LIBNAME MALIB "c:\temp\MALIB";

DATA MALIB.TESTDS ;
INFILE "c:\temp\malib\testds.txt";
INPUT VAL1 VAL2;
RUN;

Le fichier sera importer dans la bibliothèque MALIB et le dataset sera TESTDS


Il est bien sûr possible de compliquer les choses et d'importer des fichiers plus classiques comme les fichier csv ou tab delimited.

Considérons le fichier testds2.txt qui contient un enregistrement indiquant le nom des variables avec un 'tab' comme séparateur de champs:



Ici il faudra préciser à SAS le délimiteur et le fait que les observations commencent à la seconde ligne, pour cela on utilise deux options utiles dans le predicat INFILE qui sont DLM et FIRSTOBS.

LIBNAME MALIB "c:\temp\MALIB";

DATA MALIB.TESTDS2 ;
INFILE "c:\temp\malib\testds2.txt" DLM="09"x FIRSTOBS=2;
INPUT ID EVOLUTION;
RUN;

Remarquez que SAS utilise la convention "09"x pour spécifier un délimiteur de type tab. Remarquez aussi que contrairement à l'usage en cours chez les informaticiens, SAS commence à compter ses enregistrements à partir de 1 (et pas à partir de 0). La première observation valide est donc à la deuxième ligne...

Le résultat:


La suite un autre jour...

mardi 15 février 2011

MIDAS: a Delphi Three-Tiers approach

Three tiers architecture are generally defined by a Client thier (first thier), an application server (that contains the business logic and the database connection) and a database that stores the persistent information.

Some years ago Borland was proposing a quite easy approach to get benefit from those architectures.

Client computers
They know nothing about the database and database connection. They can access dataset but they don’t know the connection string. (The business logic remains in the Client software, but the dataset are contained in the application)

AppServer
contains the connection string and all the dataset required by the client on the database server.
Advantage : you have to install ADO (MDAC) components on the client machines and more important if configuration changes you have not to reconfigure all the computer park (if you have 2 machines it is ok, but if you have more, it can cost time)=>Zero configuration of thin clients.
You can also safe costs if you have to pay license for connection on the database server per client (because only one server is connected). This depends on the licensing mode.

The database server
Typically it stores the data on secure, transactional and persistent way. It can also do some business logic by using stored procedure.

Technical approach
Generally an application is based on component(s) that implement the interface IAppServer (provided by the Delphi MIDAS Multi tIer Distributed application Service most referred as DataSnap). So to create a Application Server you have to create at least one COM component. In the MIDAS philosophy the COM component (CoAppServer) contains the Dataset. We can create those dataset at design time and configure them with the proper database connection or connection string. The CoAppServer, provide easy way to make the client application communicates with the Datasets contained in the CoAppServer. Practically the client application contains a ClientDataset which is an image of the DataSet contained on the CoAppServer. The communication between the client and server can be done by DCOM, TCP/IP, HTTP, CORBA. The CoAppServer ensure that the communication is bidirectional so you can read and update dataset from the client without having an actual connection on the database itself. The only things you need is a copy of the midas.dll on the client and a valid CoAppServer that you can access.

Oui, je sais ça date un peu, mais bon nostalgie quand tu nous tiens ;-) ... Je n'ai pas eu le temps de traduire ce vieux document qui date de quelques années en français mais après l'avoir lu vous saurez que Midas n'était pas uniquement un roi qui par une grâce divine transformait tout ce qu'il touchait en or... En parlant de grâce divine, ce n'était pas si gracieux puisqu'il ne pouvait plus manger. Il pu heureusement pour lui se débarasser de ce don en se lavant les mains dans le fleuve Pactole qui depuis lors, selon la légende charie de l'or...

lundi 14 février 2011

Introduction à SAS

SAS (développé par SAS Institute depuis 1976) est un langage de programmation procédural dédidié à des traitements des données spécifiques (analyse de donnée, stockage, fusion, rapport, ...).

SAS définit deux concepts important qui forment le corps d'un programme SAS:
Le DATA block et le PROC block, ceci sépare clairement les données et les traitements à effectuer sur ses données. Un block se termine toujours par l'instruction RUN;

Le block donnée contient la définition des données, les types, formats, ... Les données seront stockées dans une bibliothèque : la SAS library.

Le block traitement (PROC) quant à la lui définit une série d'opération à effectuer sur ses données, comme par ex des tris, de la regréssion linéaire ou de l'ANOVA.

SAS dispose d'une syntaxe assez rigide. Chaque procédure dispose d'options spécifiques qu'il faut combiner judicieusement pour obtenir les résultats voulus.

Format de données en SAS
La première opération à effectuer lorsque l'on veut réaliser un programme SAS est de créer ou importer les données sur lesquelles on veut effectuer des opérations. Ces données seront contenues dans des variables dont il faudra définir le type (continue, catégorielle) mais aussi le format (date, variable de compte, montant en $ ou euro, ...)
Une fois le format défini SAS présentera toujours les valeurs en accord avec ce format pour la variable à afficher.

Formats courants:
Remarquez qu'en SAS il y a toujours un point dans les noms de format. Soit au milieu, soit à la fin.
L'instruction FORMAT s'utilise en précisant le nom de la variable et le format à lui appliquer.
FORMAT variable format

numérique : w.d : désigne le nombre de chiffres et le nombre de décimales:w : designe le nombre totale de chiffre, d désigne le nombre de chiffres destiné aux décimales.

Exemple:

DATA _NULL_;
FORMAT X 6.2;
X=101.25;
PUT X;
RUN;

affichera 101.25 (on utilise 6 car la virgule est comptée dedans) à la fin du log.
Il est aussi possible d'utiliser:
DOLLARw.d comme indicateur de format cela aura pour effet d'afficher un $ devant la valeur.

Alphanumérique: $w.; w indique le nombre de caractère.

Exemple:

DATA _NULL_;
FORMAT nm $6.;
nm="coucou";
PUT nm;
RUN;

Formats prédéfinis:
DATE9.; utilisé pour afficher des dates au format : ddMMMYYYY
DDMMYY10.; affiche une date au format dd/MM/YY
WORDDATX20.; affiche une date au format ddMONTHYYYY

SAS supporte par défaut un nombre assez impressionnant de format prédéfini, toutefois il est parfois utile de créer ses propres formats customisés.

Formats customisés:
Exemple: créer des labels pour une variable catégorielle. Les format doivent être stockés dans une bibliothèque (library) par défaut ce sera la bibliothèque WORK, qui sera détruite quand SAS sera fermé. Pour créer un format il faut utiliser une procédure:
PROC FORMAT LIBRARY=

Exemple:
PROC FORMAT ;
VALUE $SEXE 'G'='Garçon' 'F'='Fille';
RUN;

DATA _NULL_;
FORMAT S $SEXE.;
S='G';
PUT S;
RUN;

Le résultat affiché sera Garçon.

dimanche 16 janvier 2011

Les distributions dans R

La distribution uniforme
runif : random uniform : renvoit une série de nombre aléatoire compris entre les limites d’un intervalle
Y=runif(100,-2,2)
Renvoie dans Y 100 nombres aléatoires compris entre -2 et 2. Les paramètres sont le nombre de nombre aléatoire et la taille de l'intervalle dans laquelle les données seront tirées. Remarquez la fréquence d'une distribution uniform vaut 1 dans l'intervalle [0,1] et 0 partout ailleurs.
runif(1,0,1) correspond à la fonction rand() des langages de programmation courant (une seule valeur sera rendue).

La distribution normale
rnorm : random normal : renvoit une série de nombre aléatoire suivant une distribution normale de moyenne u et d’écart type s. Attention il s'agit de l'écart type, pas de la variance.
X=rnorm(100,0,1)
Dans ce cas de figure on ne précise pas les limites max et min elles sont déterminées par la distribution de probabilité de la fonction normale répondant aux paramètres entrés.

dnorm : donne un vecteur contenenant les fréquences de cette distribution.
ex:
Y=dnorm(x)
plot(Y) affichera une gaussienne de moyenne 0 et d'écart type 1.

qnorm(p, mu,sigma): donne la valeur du quantile. Soit la proposition P(x<=xq)=p, qnorm donnera la valeur de xq en fonction de p.
Dans le même ordre d'idée pnorm(xq,mu,sigma) donnera la p-value associée à xq dans la proposition précédente. Ces fonctions sont très utiles pour les réaliser des tests d'hypothèses dont la variance de la population théorique est connue.
Dans le cas contraire (variance inconnue), il est toujours possible d'utiliser pt(xq,df).
df étant le degré de liberté (n-1) dans le cas d'un test t, avec n étant la taille de l'échantillon. On suppose bien sur que la statistique de test donnant xq est une statistique standardisée.

Autres distributions :binom, chisq (khi carré), lnorm (log normale), pois, t, weibul, wilcoxon

mardi 4 janvier 2011

Considérations sur MVC


MVC est un pattern définit par un Modèle, un Viewer et un Controller. Pour faire simple:

Le schéma n'est pas des mieux pour l'instant mais globalement, le controlleur s'occuppe des entrées utilisateurs (requête, update, ...)
Le controlleur va donc créer un Modèle qui sera utilisé pour alimenter le viewer.
Le modèle quant à lui s'occupe de différentes choses
- validation
- accès aux données
- business logic
- structure des données à afficher (présentation)
Le viewer quant à lui affiche les sorties (rendu). Notez toutefois que le viewer est un observeur du Modèle. Donc si le modèle change, le viewer est automatiquement notifié et prié de ce mettre à jour. Et ce sans l'intervention du contrôleur.

Cette description est très simplifiée mais elle donne une idée rapide de ce qu'est MVC. A ce pattern on peut retrouver différents framework associé tels que Spring par exemple qui est utilisé pour implémenter MVC dans des applications Java.

Nous avons vu que le modèle (M) permet l'accès aux données, pour se faire on peut aussi utiliser un framework tel que Doctrine qui permet de simplifier le mapping des classes sur des tables (il joue le rôle des DBObject dans TrialXS).
Doctrine est un framework qui implémente le pattern Active Record (cfr Folwer)
Le pattern active record présente un certain nombre de défaut:
- mal fichu pour des queries complexe
- à chaque nouvelle table il faut une nouvelle classe

Il est toutefois déconseiller de n'utiliser que Doctrine comme modèle car chaque fois que l'on va créer un nouveau controlleur il faudra définir dans le controlleur la manière dont les données sont liées, là on s'éloigne de la philosophie MVC car la collaboration d'objets pour réaliser une fonction doit se trouver dans le modèle.
Le modèle abstrait la manière dont les données sont liées entre elles en fournissant une interface de functionalités.

De plus Active Record expose les méthodes CRUD (Create Read Update Delete). Dans certains cas ça peut créer des racourcis funestes... En effet si par exemple ajouter un nouveau user implique de mettre à jour une table d'historique, le fait d'accéder directement à la table user peut ommettre cet update (si il n'y a pas de trigger sur la table user...).