list.sort
Вы можете создать список индексов и затем вызвать list.sort
с key
:
B = [(i, j) for i, x in enumerate(A) for j, _ in enumerate(x)]
B.sort(key=lambda ix: A[ix[0]][ix[1]])
print(B)
[(0, 0), (0, 1), (1, 0), (2, 0), (2, 1), (1, 1), (2, 2), (0, 2), (2, 3)]
Примечаниечто в python-2.x, где поддерживается повторяемая распаковка в функциях, вы можете упростить вызов sort
немного:
B.sort(key=lambda (i, j): A[i][j])
sorted
Этоальтернатива вышеприведенной версии и генерирует два списка (один в памяти, на котором затем работает sorted
для возврата другой копии).
B = sorted([
(i, j) for i, x in enumerate(A) for j, _ in enumerate(x)
],
key=lambda ix: A[ix[0]][ix[1]]
)
print(B)
[(0, 0), (0, 1), (1, 0), (2, 0), (2, 1), (1, 1), (2, 2), (0, 2), (2, 3)]
Производительность
По многочисленным просьбам, добавив немного времени и сюжет.
![enter image description here](https://i.stack.imgur.com/lbAJ1.png)
Из графика видно, что вызов list.sort
более эффективен, чемsorted
.Это связано с тем, что list.sort
выполняет сортировку на месте, поэтому при создании копии данных, которая есть у sorted
, нет затрат времени / пространства.
Функции
def cs1(A):
B = [(i, j) for i, x in enumerate(A) for j, _ in enumerate(x)]
B.sort(key=lambda ix: A[ix[0]][ix[1]])
return B
def cs2(A):
return sorted([
(i, j) for i, x in enumerate(A) for j, _ in enumerate(x)
],
key=lambda ix: A[ix[0]][ix[1]]
)
def rorydaulton(A):
triplets = [(x, i, j) for i, row in enumerate(A) for j, x in enumerate(row)]
pairs = [(i, j) for x, i, j in sorted(triplets)]
return pairs
def jpp(A):
def _create_array(data):
lens = np.array([len(i) for i in data])
mask = np.arange(lens.max()) < lens[:,None]
out = np.full(mask.shape, max(map(max, data))+1, dtype=int) # Pad with max_value + 1
out[mask] = np.concatenate(data)
return out
def _apply_argsort(arr):
return np.dstack(np.unravel_index(np.argsort(arr.ravel()), arr.shape))[0]
return _apply_argsort(_create_array(A))[:sum(map(len, A))]
def agngazer(A):
idx = np.argsort(np.fromiter(chain(*A), dtype=np.int))
return np.array(
tuple((i, j) for i, r in enumerate(A) for j, _ in enumerate(r))
)[idx]
Код производительности производительности
from timeit import timeit
from itertools import chain
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
res = pd.DataFrame(
index=['cs1', 'cs2', 'rorydaulton', 'jpp', 'agngazer'],
columns=[10, 50, 100, 500, 1000, 5000, 10000, 50000],
dtype=float
)
for f in res.index:
for c in res.columns:
l = [[1, 1, 3], [1, 2], [1, 1, 2, 4]] * c
stmt = '{}(l)'.format(f)
setp = 'from __main__ import l, {}'.format(f)
res.at[f, c] = timeit(stmt, setp, number=30)
ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");
plt.show();