Заполнение SortedLIst из массива n×2
У меня есть пустой массив с формой n×2, набор кортежей длины 2, который я хотел бы перенести в SortedList. Таким образом, цель состоит в том, чтобы создать SortedList с целочисленными кортежами длины 2.
Проблема в том, что конструктор SortedList проверяет значение истинности каждой записи. Это прекрасно работает для одномерных массивов:
In [1]: import numpy as np
In [2]: from sortedcontainers import SortedList
In [3]: a = np.array([1,2,3,4])
In [4]: SortedList(a)
Out[4]: SortedList([1, 2, 3, 4], load=1000)
Но для двух измерений, когда каждая запись является массивом, нет ясного значения истинности, и SortedList не работает:
In [5]: a.resize(2,2)
In [6]: a
Out[6]:
array([[1, 2],
[3, 4]])
In [7]: SortedList(a)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-7-7a4b2693bb52> in <module>()
----> 1 SortedList(a)
/home/me/miniconda3/envs/env/lib/python3.6/site-packages/sortedcontainers/sortedlist.py in __init__(self, iterable, load)
81
82 if iterable is not None:
---> 83 self._update(iterable)
84
85 def __new__(cls, iterable=None, key=None, load=1000):
/home/me/miniconda3/envs/env/lib/python3.6/site-packages/sortedcontainers/sortedlist.py in update(self, iterable)
176 _lists = self._lists
177 _maxes = self._maxes
--> 178 values = sorted(iterable)
179
180 if _maxes:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Мой текущий обходной путь - преобразовать каждую строку в кортеж вручную:
sl = SortedList()
for t in np_array:
x, y = t
sl.add((x,y))
Тем не менее, это решение предлагает некоторые возможности для улучшения. У кого-нибудь есть идеи, как решить эту проблему без явной распаковки всех массивов в кортежи?
1 ответ
Проблема не в том, что проверяется истинность значений массивов, а в том, что они сравниваются, чтобы их можно было отсортировать. Если вы используете операторы сравнения для массивов, вы получите их обратно:
>>> import numpy as np
>>> np.array([1, 4]) < np.array([2, 3])
array([ True, False], dtype=bool)
Этот результирующий логический массив на самом деле является массивом, значение истинности которого проверяется sorted
,
С другой стороны, та же самая операция с кортежами (или списками) выполнит сравнение элементов с элементами и вернет единственное логическое значение:
>>> (1, 4) < (2, 3)
True
>>> (1, 4) < (1, 3)
False
Так когда SortedList
пытается использовать sorted
на последовательности numpy
для массивов, он не может выполнять сравнение, потому что он требует, чтобы операторы сравнения возвращали одиночные логические значения.
Одним из способов абстрагирования было бы создание нового класса массива, который реализует операторы сравнения, такие как __eq__
, __lt__
, __gt__
и т. д. для воспроизведения поведения сортировки кортежей. По иронии судьбы, самый простой способ сделать это - преобразовать базовые массивы в кортежи, например:
class SortableArray(object):
def __init__(self, seq):
self._values = np.array(seq)
def __eq__(self, other):
return tuple(self._values) == tuple(other._values)
# or:
# return np.all(self._values == other._values)
def __lt__(self, other):
return tuple(self._values) < tuple(other._values)
def __gt__(self, other):
return tuple(self._values) > tuple(other._values)
def __le__(self, other):
return tuple(self._values) <= tuple(other._values)
def __ge__(self, other):
return tuple(self._values) >= tuple(other._values)
def __str__(self):
return str(self._values)
def __repr__(self):
return repr(self._values)
С помощью этой реализации вы можете теперь отсортировать список SortableArray
объекты:
In [4]: ar1 = SortableArray([1, 3])
In [5]: ar2 = SortableArray([1, 4])
In [6]: ar3 = SortableArray([1, 3])
In [7]: ar4 = SortableArray([4, 5])
In [8]: ar5 = SortableArray([0, 3])
In [9]: lst1 = [ar1, ar2, ar3, ar4, ar5]
In [10]: lst1
Out[10]: [array([1, 3]), array([1, 4]), array([1, 3]), array([4, 5]), array([0, 3])]
In [11]: sorted(lst1)
Out[11]: [array([0, 3]), array([1, 3]), array([1, 3]), array([1, 4]), array([4, 5])]
Это может быть излишним для того, что вам нужно, но это один из способов сделать это. В любом случае, вам не сойдет с рук использование sorted
на последовательности объектов, которые не возвращают единственное логическое значение при сравнении.
Если все, что вам нужно, это избегать цикла for, вы можете просто заменить его на понимание списка (т.е. SortedList([tuple(row) for row in np_array])
).