Skip to content

Group By

Objectifs

L'opération GroupBy que vous connaissez bien en R (Tidyverse) ou en Python (Panda) répond à un pattern très courant en Data Science, à savoir la stratégie du split apply combine


Exemple de Split-Apply-Combine tiré de [Python For Data Analysis](https://wesmckinney.com/book/data-aggregation)

Warning

Attention, ici l'objectif c'est de ne pas utiliser les fonctions groupby déjà existante en Python, en Panda, ou en Numpy.

Minimun

  • Reprendre l'algorithme vu en cours pour compter le nombre d'occurences et l'adapter pour gérer une liste de chiffres ou de lettres.
  • En faire une fonction groupby(...) qui pour une liste passée en paramètre renvoie une liste qui contient pour chaque occurence unique, la clef, et le nombre d'occurences. Ex : ["A","A","B","C","A","B"] renverra [["A",3], ["B",2],["C",1]]
  • La liste passé en paramètre devient la liste contenant les clefs, auquel on associe une deuxième liste qui contient les valeurs. La fonction group_by(... , ...) prend donc deux entrées. Ex: la liste clef ["A", "A", "B", "C", "A", "B"] et la liste de valeurs associées [2,3,5,1,7,4]. La fonction renvoie pour chaque clef la liste des valeurs associées [["A",[2,3,7]], ["B", [5,4]], ["C", [1]]]
  • Créer une autre fonction compute_sum (...) qui prend cette liste renvoyée par votre première fonction (ex : [["A",[2,3,7]], ["B", [5,4]], ["C", [1]]]) et renvoie une somme (ex: [["A",12], ["B", 9], ["C", "1"]])

Bonus

  • Simplifier/Revoir cette fonction en changeant de type de structure de données : les Dictionnaires (voir ici) permettent de gérer beaucoup plus facilement les couples {clef: valeurs}
  • Passer une fonction existante en python (ex: sum, mean, etc. ) comme troisième entrée de la fonction groupby(..., ... , ... ). Celle-ci sera appliqué pour l'aggrégation des termes de la liste, par clef. Le retour sera fonction de l'opération appliquée.

Correction

Version avec des listes

def simple_groupby(tabKeyOriginal,tabData):
    tabKey = tabKeyOriginal[:]
    tabKey.sort()
    tabUniqueKey = []
    tabGrouped = []

    tabUniqueKey.append(tabKey[0])

    for i in range(1,len(tabKey) ):
        if (tabKey[i] != tabKey[i - 1]):
            tabUniqueKey.append(tabKey[i])

    tabOccurence = []
    for sd in range (len(tabUniqueKey)):
        tabOccurence.append([])
        for ad in range(len(tabKeyOriginal)):
            if (tabUniqueKey[sd] == tabKeyOriginal[ad]):
                tabOccurence[sd].append(tabData[ad]) 

    for i in range(len(tabUniqueKey)):
        tabGrouped.append([tabUniqueKey[i], tabOccurence[i]])

    return (tabGrouped)
def compute_sum(groupedData):
    tabSummed = []
    for i in range(len(groupedData)):
        tabTransformed = []
        tabTransformed.append(groupedData[i][0])
        sommeElement = 0
        for e in groupedData[i][1]:
            sommeElement += int(e)
        tabTransformed.append(sommeElement)
        tabSummed.append(tabTransformed)
    return tabSummed

L'appel à ces deux fonctions :

myList = ["A","B","A","C","D","B","D","A","A","D","C","C","E","D","C"]
myData = [ 2,  8,  3,  5,  2,  3,  7,  10, 5,  2,  4,  3,  8,  5,  7]
# => ["A",[2,3,10,5]]
result = simple_groupby(myList,myData)
print("list only = ", result)
resultsummed = compute_sum(result)
print("list only summed = ", resultsummed)

Version avec des dictionnaires

def dict_groupby(some_keys,some_data):
    group_list = {}
    for i, k in enumerate(some_keys):
        if k in group_list.keys():
            group_list[k].append(some_data[i])
        else:
            group_list.update({k: [some_data[i]]})
    return group_list
def dict_groupby_sum(some_keys, some_data):
    result = {}
    for key, val in zip(some_keys, some_data):
        if key not in result:
            result[key] = 0
        result[key] += val
    return result

L'appel aux deux fonctions :

result = dict_groupby(myList, myData)
print("dict only", result)

#https://jakevdp.github.io/blog/2017/03/22/group-by-from-scratch/
result = dict_groupby_sum(myList, myData)
print("dict only summed = ", result)