1
votes

Encodage delta rapide pour une séquence croissante d'entiers en Python

Donné a = [1, 2, 3, 4, 5]

Après l'encodage, a '= [1, 1, 1, 1, 1] code >, chaque élément représente la différence par rapport à son élément précédent.

Je sais que cela peut être fait avec

for i in range(len(a) - 1, 0, -1):
    a[i] = a[i] - a[i - 1]

Y a-t-il un moyen plus rapide? Je travaille avec 2 milliards de chiffres ici, le processus prend environ 30 minutes.


0 commentaires

3 Réponses :


2
votes

Vous pouvez utiliser numpy.diff , par exemple :

import numpy as np
a = [1, 2, 3, 4, 5]
npa = np.array(a)
a_diff = np.diff(npa)


0 commentaires

3
votes

Vous pouvez utiliser zip pour rassembler la liste avec une version offset et soustraire ces valeurs

a = [a[0], *[nxt - cur for cur, nxt in zip(a, islice(a, 1, None))]]

Résultat:

[1, 1, 1, 1, 1]


3 commentaires

AFAIK, a [1:] créera une nouvelle liste, ce qui peut entraîner une surcharge importante si la liste d'origine est très grande. J'ai testé votre code avec islice et il devient aussi rapide que l'approche starmap .


Intéressant, vous avez raison, cela améliore considérablement les choses. j'aime ce type de questions - vous apprenez tellement ...


Je l'aime aussi; cela vous fait vraiment fouiller dans les codes XD.



3
votes

Une manière en utilisant itertools.starmap , islice et operator.sub:

l = list(range(1, 100000000))

# OP's method
%timeit [l[i] - l[i - 1] for i in range(len(l) - 1, 0, -1)]    
# 14.2 s ± 373 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# numpy approach by @ynotzort
%timeit np.diff(l)
# 8.52 s ± 301 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# zip approach by @Nick
%timeit [nxt - cur for cur, nxt in zip(l, l[1:])]
# 7.96 s ± 243 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

# itertool and operator approach by @Chris
%timeit [l[0], *starmap(sub, zip(islice(l, 1, None), l))]
# 6.4 s ± 255 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Résultat:

[1, 1, 1, ..., 1]

Benchmark :

from operator import sub
from itertools import starmap, islice

l = list(range(1, 10000000))

[l[0], *starmap(sub, zip(islice(l, 1, None), l))]


6 commentaires

C'est environ 40% plus rapide que le code numpy et zip à 20 millions de valeurs ... (voir ma note sur l'analyse comparative)


Il est intéressant que vos résultats montrent que mon code surpasse @ynotzort. Je me demande si cela est lié à la quantité de mémoire sur chaque machine.


@Nick Avez-vous inclus la création de la liste en tant que partie de tableau numpy ( np.array (l) )?


Je l'ai inclus dans la configuration pour qu'il ne s'exécute qu'une seule fois.


@Nick Pour la partie numpy , convertir une liste en numpy.ndarray prend plus de temps que de faire le diff, et j'ai pensé que ce serait une comparaison juste d'inclure cette conversion dans le timeit . C'est peut-être là que la différence a été faite.


C'est raisonnable; si les données originales sont dans une liste, vous devrez payer ce prix pour faire le diff . Je n'avais pas réalisé que c'était si cher non plus.