XD blog

blog page

~technical


2013-11-21 Fusionner deux tableaux

L'exercice est le suivant : on récupère deux tableaux depuis le site http://www.data.gouv.fr/. On récupère les deux fichiers suivants :

Ils contiennent des effectifs de la fonction publique ventilés par ministères :

Ministère    Libellé    Catégorie d'emploi    Emploi    ETPPLF
1    Affaires étrangères    1101    Titulaires et CDI en administration centrale    3 059
1    Affaires étrangères    1102    Titulaires et CDI dans le réseau    2 895
1    Affaires étrangères    1103    CDD et volontaires internationaux    2 877
1    Affaires étrangères    1104    Militaires    712
1    Affaires étrangères    1105    Agents de droit local    4 962
...

On cherche à mesurer l'évolution des effectifs entre ces deux années même si la liste des ministères et des catégories évolue.


more...

2013-11-09 Compter les pièces de monnaie pour obtenir un montant

On est amené presque tous les jours à compter les pièces dans son porte-monnaie pour payer la baguette, le fromage ou la bouteille de vin. Comment écrire un algorithme qui donne la liste des pièces qu'il faut piocher dans son porte-monnaie pour payer ? On s'intéressera seulement au cas où on cherche à composer le montant juste. Cela revient à chercher un sous-ensemble S de pièces dans l'ensemble P des pièces du portefeuille pour composer le montant M :

 M = \sum_{ p \in S } p

Une façon naïve de construire l'ensemble S est de procéder comme on le fait souvent, c'est-à-dire à prendre la pièce la plus grosse, de voir si elle dépasse le montant N puis de passer à la seconde plus grande et ainsi de suite. Soit :

Cela donne le programme suivant :


more...

2013-11-08 De l'idée au programme informatique

Lorsqu'on apprend à programmer, on a souvent une idée précise de l'objectif à atteindre et pourtant, on reste perplexe devant l'écran ne sachant pas par où commencer. Par exemple, si on dispose d'une matrice de trois colonnes :
xypoids
AC3
AD1
AE4
BD6
Et on souhaite calculer combien de fois on a le couple (x,y) divisé par combien de fois on a x :

 \frac{ \# (x,y) } { \# x }

Par exemple, si x=A et y=D, on aura 1 / (3 + 1 + 4) = 0.125. Maintenant, comment s'y prend-on ?

Tout d'abord, on cherche à calculer un ratio de trucs qui ne sont pas des nombres entiers. Un dictionnaire est tout indiqué pour stocker ces trucs car il permet d'associer n'importe quoi (valeur) à presque n'importe quoi (clé). La valeur est une somme d'entiers, la clé est un couple de lettres ou une lettre. Sans me soucier des petits détails, voilà ce que j'ai envie d'écrire comme premier jet :

l = [ ['A','B', 3], ['A','C', 1], 
      ['A','E', 4], ['B','D', 6], ]

def transition(l):
    d = {}
    for x,y,n in l :
        d [x,y] += n  # erreur ici : KeyError: ('A', 'B')
        d [x] += n
    
    for x,y in d :
        d[x,y] /= d [x]
    return d
    
print (transition(l))

Bien sûr ça ne marche pas (voir l'erreur ci-dessus). Mais j'ai foi en moi. La logique est bonne. Lorsque j'écris d [a,b] += n, je suppose que le couple (x,y) existe dans le dictionnaire même la toute première fois. Et c'est pas possible ! Il est vide au début.


more...

2013-11-05 Python, SQL, pandas and frustration

When I try to avoid coding and to use existing tools, I usually decompose my problem in smaller ones I think people already solved. I usually skip the documentation until I find an example for each of them. That's how I proceed to use pandas. I imagine how I would do it using SQL and I try to write it using this module. My problem was the following one:

SELECT location, MIN(commute) AS minc, MAX(commute) AS maxc FROM data

Which I was able to solve this way (after a couple of tries and a glass of wine to avoid losing my temper):

data = [ 
    { "name":"hugo", "location":"paris", "commute":55 },
    { "name":"marcel", "location":"paris", "commute":45 },
    { "name":"jack", "location":"lyon", "commute":25 },    
    { "name":"hugo", "location":"lyon", "commute":20 },  ]

df = pandas.DataFrame(data)

agg = df.groupby ( "location", as_index=False).agg ( 
            { "commute": [ ("minc",min), ("maxc", max) ] } ) # first glass of wine
print (agg)

Then, I wanted to write this:

SELECT A.*, B.minc, B.maxc FROM data AS A INNER JOIN ( 
        SELECT location, MIN(commute) AS minc, MAX(commute) AS maxc FROM data) AS B 

Which I converted into after a second glass of wine:

agg = df.groupby ( "location", as_index=False).agg ( 
            { "commute": [ ("minc",min), ("maxc", max) ] } )
agg.columns = ["minc", "maxc"]  # the second glass of wine
join = df.merge (agg, left_on="location", right_on = "location")
print (join)

But you could write it that way:

agg = df.groupby ( "location").agg ( 
            { "commute": [ ("minc",min), ("maxc", max) ] } )
agg.columns=["minc","maxc"]
join = df.merge (agg, left_on="location", right_index= True)
print (join)

Last precision: the instruction agg.columns=["minc","maxc"] rename the column name. However, it works in this case just because we aggregated only one column (commute). When several columns are aggregated, the order of the column in the resulting matrix is not always the same. So do not do the following:

def sums(l) : return ",".join(l)
    
agg = df.groupby ( "location", as_index=False).agg (
            { "name":sums, 
              "commute": [ ("minc",min), ("maxc", max) ] } )
agg.columns=["name","minc","maxc"]  # glass of whisky

I often get frustrated about tools, it takes me so many tries to get the data the way I want before I start working on an algorithm using them. Then, you try to apply it on a much bigger dataset and it fails for a couple of bad rows which cannot be parsed because of a string including tabs. After, I got the accent problem... I hate those days.

2013-11-02 Stop a thread in Python

I was writing a simple GUI in Python, a button start to start a function from a thread, another one to stop the execution. I then realized my program did not stop.

import threading, time

class MyThread(threading.Thread):
    def run(self):
        while True:
            time.sleep(1)
            
th = MyThread()
th.start()

There are two ways to avoid that issue. The first one is to set up daemon to True.

th = MyThread()
th.daemon = True
th.start()

The second way is to use a kind of hidden function _stop(not documented). But this function might disappear in the next version of python.

th = MyThread()
th.start()
th._stop()

2013-10-31 Choisir des couleurs pour un graphe

J'ai parfois besoin de pas mal de couleurs sur un graphique (avec matplotlib par exemple) et je ne sais jamais quelles couleurs choisir après rouge, vert, bleu. Bref, j'imagine que dans la liste suivante, si chaque couleur porte un nom, c'est qu'on peut les reconnaître.


more...

2013-10-27 Convert HTML into JSON

I needed to convert a HTML string into JSON. After looking for results without any success, I thought doing it myself should be faster than searching. As an exemple:

content = '<html><body><div class="an_example"><p>one paragraph</p></div></body></html>'
js = HTMLtoJSONParser.to_json(content)
print (js)
Will produce this:
{'html': {'body': {'div': {'p': {'': 'one paragraph'}, '#class': 'an_example'}}}}

The implementation of HTMLtoJSONParser follows:


more...

2013-10-23 Using pythonnet

I migrated over Windows 8.1 and I realized my version of pythonnet for Python did not work anymore. I got the following error:

ImportError: dynamic module does not define init function (PyInit_clr)
I did not really try to understand but I knew I had to access the sources first because the official website of pythonnet does not provide any version for python 3.x. So I looked for pythonnet on github, I found this. I got the sources which did not compile on the first try unless I apply the three following modifications: After the compilation completed (on Windows 8.1 + Visual Studio 2012, 2013), I could use the two files clr.pyd and Python.Runtime.dll the same way. There were some others modifications I do not remember exactly. Anyway, my version is located here sdpython/pythonnet.

2013-10-14 Mon PC est mort ce soir

Je fais régulièrement des sauvegardes qui ne servent à rien la plupart du temps à rien sauf quand le disque dur de mon PC me lâche. Là, je suis très content de pouvoir continuer sur un autre portable en recopiant mes données même s'il faut que j'installe une tonne de logiciels (python, latex, office, 7zip, ...). Ca m'est arrivé il y a quelques jours et j'ai appréhendé la situation avec calme même si j'ai été surpris que mon portable lâche après seulement six mois.

Sauvegarder peut être terriblement contraignant. Il y a bien sûr des solutions style stockage distant. Mais l'offre gratuite est limitée à 5 Go et étudiée pour qu'elle soit dépassée dès qu'on y stocke ses photos. De plus, je n'aime pas trop me dire que mes données sont répliquées trois fois à tous les coins du monde.

J'en suis donc revenu aux solutions artisanales. On sauvegarde si cela n'est pas trop coûteux de même qu'on écrit des articles de blogs si cela ne pas prend pas trop de temps de les mettre en ligne. Donc, ça doit se faire en trois clics de souris au plus. Une autre chose utile est la possibilité de garder l'historique des dernières modifications apportées à un document. On efface par mégarde, on supprime un paragraphe qu'on ne voulait pas supprimer...

Pour ces deux raisons, j'en suis venu à utiliser Tortoise SVN. Tous les fichiers que je souhaite conserver y sont archivés. Et je peux récupérer facilement l'historique. Pour le reste, j'ai codé moi-même la sauvegarde (voir synchronize). Elle récupère la liste des fichiers recensés par Tortoise SVN et recopie les fichiers modifiés depuis la dernière sauvegarde sur un disque dur externe.

Toutes mes copies ne sont pas cryptées mais je contrôle les supports sur lesquels mes données sont recopiées. J'utilise malgré tout des services style dropbox ou skydrive pour facilement transbahuter certains depuis mon ordinateur vers mon téléphone portable (la lecture pour le métro).

2013-10-02 Ways to show code in a blog post

I found another way to show code in a blog. It looks nicer than prettify. The only change is to indicate the langage you want to use to highlight you piece of code. It is called SyntaxHighlighter.

You need to insert the following lines in the header:

<link href="/js/shCore.css" rel="stylesheet" type="text/css" />
<link href="/js/shThemeDefault.css" rel="stylesheet" type="text/css" />
<script src="/js/shCore.js" type="text/javascript"></script>
<script src="/js/shAutoloader.js" type="text/javascript"></script>
And those one at the end of the body:
<script type="text/javascript">
SyntaxHighlighter.autoloader(
  'js jscript javascript /js/shBrushJScript.js',
  'applescript /js/shBrushAppleScript.js'
);
 
SyntaxHighlighter.all();
</script>
Then to highlight a piece of code:
<pre class="brush: js">
var your_code_here='simple try';
</pre>

I also tweaked the css style to reduce the size in shCore.css:

.syntaxhighlighter textarea {
  ...
  font-size: 0.98em !important;
  ...
}
And to remove the green question mark, in shThemeDefault.css:
.syntaxhighlighter .toolbar {
  /*color: white !important;
  background: #6ce26c !important;
  border: none !important;*/
  display: none;
}

2013-09-29 Zoomer sur graphe XY avec d3.js

d3.js est un framework écrit en javascript qui permet de visualiser des données. Il est conçu pour des sites web et permet de créer des graphes animés. Le résultat est assez esthétique à en juger par les différentes galleries :

Tout d'abord, je dois dire que ça prend du temps de concevoir un graphe. Mon objectif était de créer un graphe XY et de pouvoir zoomer horizontalemen sur une partie. Quand on découvre l'outil et les spécifités de javascript, il est préférable de partir d'une exemple qui fonctionne déjà sans trop chercher à aggréger des bouts de codes venant de plusieurs graphes. L'exemple que j'ai trouvé pour le zoom vient d'ici : js fiddle. Appliqué à mes données, cela donne :

Les premiers pas sont tout de même assez longs, les erreurs ne sont pas faciles à comprendre, surtout quand on est novice. On espère que ce graphe qu'on est en train de constuire pourra resservir. Cela dit voici, quelques astuces.


more...

2013-09-28 Utiliser l'API Linkedin

J'ai utilisé l'API Linkedin pour voir s'il était possible de récupérer mes contacts et de les mettre dans un fichier Excel. Tout d'abord il faut récupérer une clé d'identification en suivant les instructions décrites REST. Cette clé vous donne la possibilité de consulter votre profil, d'effectuer des recherches, de récupérer la liste des contacts, de faire tout ce que votre compte vous permet de faire excepté que vous pouvez le faire jusqu'à un certain point. Par exemple, je peux récupérer l'intégralité de mes contacts, mais je ne peux effectuer qu'une dizaine de recherches et récupérer pour chacune d'entre elles les 110 premiers résultats. La limite de 110 dépend de votre compte (le mien est un compte gratuit), le nombre de résultats de recherche dépend de votre compte développeur (throttle limits). Il est possible d'étendre cette limite mais il faut devenir Partner (Request for Partnering with LinkedIn) mais je ne suis pas allé jusque là. Cela dit, les compteurs sont remis à zéro tous les jours.

Après avoir effectué quelques essais, on s'aperçoit qu'il est impossible d'accaparer des données auxquels votre compte ne vous donne pas accès. D'un point de vue utilisateur, c'est l'assurance qu'un programmeur n'aura pas accès à d'autres informations que celles dévoilées par votre profil public. Elles le seront simplement sous une autre forme (un second site web, uune feuille Excel...)

La clé que j'ai requise est une clé développeur. Elle permet de développer (un site web par exemple) mais ne permet pas de passer à l'échelle. Pour d'autres usages, il faut regarder le site Linkedin Developers.

Le code que j'ai utilisé est écrit en Python et accessible ici, documentation. Pour des pages, il est préférable d'utiliser les outils javascripts développés par Linkedin.

2013-09-27 Format de compression BZ2

Il existe beaucoup de formats de compression. Le plus connu est dans doute le format zip. Il serait impossible de tous les citer (voir compression). Un des logiciels les plus utilisés sous Windows est 7z. Il permet de compresser / décompresser les formats les plus utilisés pour la compression du texte. Et le format 7-zip est le plus souvent le plus efficace.

Alors pourquoi aurait-on besoin d'autre format de compression ? Ce qui marche pour le texte ne marche pas forcément très bien sur la musique, les images ou la vidéo. Pour le texte, on cherche toujours une compression sans erreur. On accepterait mal qu'un document se retrouve altéré durant les étapes de compression / décompression. Pour les images, on recherche principalement des compressions avec un peu de perte : une déterioration de la qualité de l'image qui soit presque invisible pour l'oeil humain. Le format MP3 est aussi une compression de la musique avec perte. Pour la vidéo qui est très gourmande, on utilise le fait que les images changent peu d'une seconde à l'autre comme le décor en fond d'écran. On ne va donc pas compresser les images directement mais la différence entre une image et sa précédente.

Pour en revenir aux données qu'on compresse sans perte, pourquoi utiliser un autre format de compression si 7-zip est meilleur ? En fait, il est meilleur la plupart du temps mais pour certains usages précis, d'autres formats sont plus adaptés, plus simples ou plus performants. Le format Bzip2 ou Bz2. Pour citer Wikipedia, les avantages de ce format sont : une faible utilisation mémoire à la compression, la robustesse des archives à la corruption et la parallélisation possible sur de nombreux threads.

Pour compresser du texte en Python au format bz2, on utilise le programme suivant :

import bz2

# les lignes de texte à décompresser
lines = ["line1", "line2 é"]

# on ouvre un fichier classique en mode binaire
with open(file,"wb") as f :
    comp = bz2.BZ2Compressor()
    for line in lines :
        tof = line + "\n"  # on ajoute une fin de ligne
                           # puis on encode en utf8 avant de compresser
        c = comp.compress ( tof.encode("utf8")  )
        f.write( c )
    c = comp.flush()   # il ne faut pas oublier car le 
    f.write( c )       # BZ2Compressor fonctionne comme un buffer

Pour le relire :

import bz2
lines = [ ]
with bz2.BZ2File(file, "r") as f :
    for line in f :
        s = str(line, encoding = "utf8")
        lines.append (s)
Avec ses deux exemples, il n'est pas besoin de conserver une version décompressée du fichier. On écrit et lit directement les données au format compressé.

2013-09-26 Busy areas in Paris

During summer, one pleasure is to go to work by bike. Simple option is to take a Velib but most of the time, the closest Velib station is empty. The same thing happens when you leave your work to go back home. No bike is available.

I thought maybe this could be used to draw a map of Paris showing areas where people work. I thought about looking at the distribution of the number of available bikes over a day. I already mentioned that the Velib data was available (see Les stations Vélib à Paris un jeudi soir). Next figure shows it for a couple of stations and one of them is clearly a working station: bikes arrive in the morning and disappear at the end of the working day (it was taken a couple of weeks ago during a week day).

The number of available bikes was measured every five minutes. Knowing that every station does not have the same number of spots, I normalized the previous curve by the sum. I then considered the sum between 10am and 4pm. So for each station, I built the following indicator:

 I(s) =  \frac{\sum_{t=10am}^{4pm} X(s,t) }              { \sum_{t=0am}^{11:59pm} X(s,t) }

I used the information to draw a map of Paris with the Velib stations. If I(s) > 0.25 , I used a red flag and a green otherwise.

Basically, companies offices are located in the center of Paris (districts with one digit) and around the Seine, people live around (districts with two digits). It also shows there are some business areas just outside Paris like Issy-Les-Moulineaux (where I work). You can play with the final result below. It uses OpenStreetMap and OpenLayers.


more...

2013-09-21 Le profile online d'un développeur

Beaucoup de sociétés recherchent de bons développeurs et les dernières années ont changé les façons de les trouver. Il y a d'abord les réseaux sociaux Linkedin, Viadeo, Facebook. Mais ce n'est pas tout, les bons développeurs ont souvent partagé leur code ou contribué à des projets open source. On retrouve leurs traces sur GitHub. Certains ont aussi tenté de résoudre certains des problèmes proposés par kaggle. Ajoutons, un site web, un petit blog pour commenter tous les défis techniques auquel un développeur s'attaque.

Il y a dix ans, le CV et la lettre de motivation était la façon de se vendre. Le CV est aujourd'hui sur Linkedin ou Viadeo, la motivation dans le nombre de commit sur GitHub.


<-- -->

Xavier Dupré