Reconciliation de données via la methode SwapLevel

SwapLevel

Afin d’illustrer l’utilisation du swaplevel, on va imaginer 2 dataframes (firstReport & secondReport) dont il faut comparer les colonnes.


secondReport = pd.DataFrame([
[1, 5, 'Eric'],
[1, 10, 'Eric'],
[1, 30, 'Marc'],
[1, 5, 'Bob'],
[2, 40, 'Eric'],
[2, 15, 'Bob']], columns = ['IncidentNumber','TimeSpent','User'])

La première étape consiste à concaténer les DataFrames horizontalement avec la fonction de concaténation et à distinguer chaque trame avec le paramètre keys


df_all = pd.concat([firstReport.set_index('IncidentNumber'), 
secondReport.set_index('IncidentNumber'),
axis='columns', keys=['First', 'Second'])

Il est probablement plus facile d’échanger les niveaux des colonnes et de mettre les mêmes noms de colonnes les uns à côté des autres:


df_final = df_all.swaplevel(axis='columns')[firstReport.columns[1:]]

Sortie

Methode Apply

Allons un peu plus loin dans la reconciliation entre les deux rapports, afin de mettre en valeur la plus grande période passé sur un incident.

On écrit dabords une méthode adapté…


def highlightGreaterTimeSpent(data,column):
    redColor = 'color: {}'.format('red')
    #other is the other dataframe to compare with
    other = data.xs(column, axis='columns', level=-1)
    return pd.DataFrame(np.where(data.gt(other, level=0), redColor, ''),
                        index=data.index, columns=data.columns)

… qu’on applique à notre df_final

df_final = df_final.reset_index(drop=True).style \
.apply(highlightGreaterTimeSpent,column='First', axis=None) \
.apply(highlightGreaterTimeSpent,column='Second',axis=None)

Notons le caractère de continuation de ligne \

La nécessité de ce style devient plus évidente à mesure que les noms de méthode s’allongent et que les méthodes commencent à prendre des arguments.

 Sortie

Rappel sur les niveaux d’index des Dataframes pandas .

Levels

Les levels font partie intégrante de l’index ou de la colonne.

Contruisons un dataframe illustrant le rapport d’ une équipe support mettant en avant le numero d’incident, le temps passé dessus, et l’utilisateur ayant travaillé sur cet incident.


firstReport = pd.DataFrame([
[1, 10, 'Eric'],
[1, 20, 'Eric'],
[1, 30, 'Marc'],
[1, 10, 'Bob'],
[2, 25, 'Eric'],
[2, 15, 'Bob']], columns = ['IncidentNumber','TimeSpent','User'])

L’index ici n’a qu’un seul niveau (il n’y a qu’une seule valeur d’index identifiant chaque ligne). L’index est artificiel (nombre courant) et se compose de valeurs de 0 à 5.

Somme

Disons que nous voulons fusionner (additionner) tous les journaux créés par le même utilisateur au même problème (pour obtenir le temps total consacré au problème par l’utilisateur)


time_logged_by_user = report.groupby(['IncidentNumber', 'User']).TimeSpent.sum()

  1. Maintenant, notre index de données a 2 niveaux, car plusieurs utilisateurs ont enregistré l’heure sur le même problème.
  2. Les niveaux sont IncientNumbet et User.
  3. Les niveaux font partie de l’index (seulement ensemble, ils peuvent identifier une ligne dans un DataFrame / Series).

Algorithme de Roy-Floyd-Warshall appliqué à la recherche de doublons.

Concidérons un tableau de n éléments qui contient des éléments compris entre  1 et n-1, l’un de ces nombres apparaissant autant de fois.

Contraintes

Trouver l’un de ces nombres répétitifs en y appliquant un algorithme de complexité O(n)
Utiliser uniquement un espace mémoire constant.

Voici donc une approche basée sur l’algorithme de recherche de cycle de Roy-Floyd-Warhall.

Nous l’utilisons pour détecter une boucle dans une liste chaînée. L’idée est de considérer les éléments du tableau comme des nœuds de liste liés. Tout index particulier pointe vers la valeur de cet index.

Et nous verrons qu’il y a une boucle comme indiqué dans l’image ci-dessous.

En cas de doublon, deux index auront la même valeur et ils formeront un cycle comme dans l’image ci-dessous.

La liste liée formée pour l’exemple  serait:

4->2->3->5->4->1

Nous pouvons donc trouver le point d’entrée du cycle dans la liste chaînée: ce sera notre élément en double.

  1. Nous maintenons deux pointeurs rapides et lents
  2. Pour chaque étape, rapide se déplacera vers l’index qui est égal à arr [arr [rapide]] (deux sauts à la fois) et lent se déplacera vers l’index arr [lent] (une étape à la fois).
  3. Lorsque rapide == lent, cela signifie que nous sommes maintenant dans un cycle.
  4. Rapide et lent se rencontreront dans un cercle et le point d’entrée de ce cercle sera l’élément en double.
  5. Maintenant, nous devons trouver un point d’entrée, nous allons donc commencer par rapide= 0 et visiter une étape à la fois à la fois rapide et lent.
  6. Lorsque rapide == lent, ce sera le point d’entrée.
  7. Renvoyer le point d’entrée.
arr = [4,2,3,5,4,1]

def findDuplicate(arr):
    slow = arr[0]
    fast = arr[arr[0]]

    while(fast != slow):
        slow = arr[slow]
        fast = arr[arr[fast]]

    fast = 0
    while(fast!= slow):
        slow = arr[slow]
        fast = arr[fast]

    return slow

res = findDuplicate(arr)
print(res) // output: 4

Complexité temporelle: O(n)

Espace auxiliaire: O(1)