У меня есть список данных в следующем виде:
[(id\__1_, description, id\_type), (id\__2_, description, id\_type), ... , (id\__n_, description, id\_type))
Данные загружаются из файлов, принадлежащих к той же группе. В каждой группе может быть несколько одинаковых идентификаторов, каждый из которых поступает из разных файлов. Меня не волнуют дубликаты, поэтому я подумал, что хороший способ сохранить все это - это поместить его в тип Set. Но есть проблема.
Иногда для одного и того же идентификатора описания могут незначительно отличаться, например:
IPI00110753
(Обратите внимание, что этот пример взят из база данных белков uniprot.)
Меня не волнует, различаются ли описания. Я не могу их выбросить, потому что есть вероятность, что используемая мной база данных белков не будет содержать список для определенного идентификатора. Если это произойдет, я захочу показать биологам понятное для человека описание, чтобы они примерно знали, на какой белок они смотрят.
В настоящее время я решаю эту проблему, используя тип словаря. Однако мне не очень нравится это решение, потому что оно использует много памяти (у меня много таких идентификаторов). Это лишь их промежуточный перечень. Идентификаторы проходят некоторую дополнительную обработку, прежде чем они будут помещены в базу данных, поэтому я хотел бы, чтобы моя структура данных была меньше.
У меня действительно два вопроса. Во-первых, получу ли я меньший объем памяти, используя для этого тип Set (над типом словаря), или мне следует использовать отсортированный список, где я проверяю каждый раз, когда я вставляю в список, чтобы увидеть, существует ли идентификатор, или есть ли Третье решение, о котором я не подумал? Во-вторых, если тип Set является лучшим ответом, как мне включить его, чтобы посмотреть только на первый элемент кортежа, а не на все?
Спасибо, что прочитали мой вопрос,
Тим
Обновлять
Основываясь на некоторых полученных мной комментариях, позвольте мне немного уточнить. Большая часть того, что я делаю со структурой данных, я вставляю в нее. Я прочитал его только дважды, один раз, чтобы добавить в него дополнительную информацию *, и один раз, чтобы вставить его в базу данных. Однако в дальнейшем может быть дополнительная аннотация, которая делается до того, как я вставлю в базу данных. К сожалению, я не знаю, произойдет ли это сейчас.
Прямо сейчас я пытаюсь сохранить эти данные в структуре, не основанной на хэш-таблице (т.е. словарях). Я бы хотел, чтобы новая структура была достаточно быстрой при вставке, но ее чтение может быть линейным, поскольку я действительно делаю это только дважды. Я пытаюсь отойти от хеш-таблицы, чтобы сэкономить место. Есть ли лучшая структура или хеш-таблица настолько хороша, насколько это возможно?
* Информация представляет собой список идентификаторов белков Swiss-Prot, который я получаю, запрашивая uniprot.






В наборах нет ключей. Элемент является ключ.
Если вы думаете, что вам нужны ключи, у вас есть сопоставление. Более или менее по определению.
Последовательный поиск в списке может быть медленным даже при использовании двоичного поиска. Отображения используют хеши и работают быстро.
Вы говорите о таком словаре?
{ 'id1': [ ('description1a', 'type1'), ('description1b','type1') ],
'id2': [ ('description2', 'type2') ],
...
}
Конечно, это кажется минимальным. ID представлены только один раз.
Возможно, у вас есть что-то подобное?
{ 'id1': ( ('description1a', 'description1b' ), 'type1' ),
'id2': ( ('description2',), 'type2' ),
...
}
Не уверен, что можно найти что-то более компактное, если не прибегать к использованию модуля struct.
Ваши примеры словарей довольно близки к тому, что у меня есть в настоящее время, однако, как я уже сказал, я ищу решение, которое не включает хеш-таблицу для сокращения объема памяти. Однако я продолжу использовать словарь, если ни одна из других структур не даст значительной экономии.
Есть и другие способы упорядочить данные, но это зависит от того, что вы делаете с данными, от ваших требований и ограничений.
Значит, мои примеры были удачной догадкой. Ваше описание довольно расплывчато относительно того, что у вас есть и в чем на самом деле проблема. Вам не хватает памяти? Или это преждевременная оптимизация?
Это не преждевременная оптимизация. Люди, использующие альфа-версию, достигли унаследованных пределов моего плохого первоначального дизайна. Это лишь один из аспектов многих дизайнерских решений, которые я не продумала.
наборы реализованы с использованием той же логики, что и хэш-таблицы, поэтому вы не получите там никакого значения.
Как насчет использования словаря {id: (description, id_type)}? Или словарь {(id, id_type): description}, если (id, id_type) является ключом.
Наборы в Python реализованы с помощью хеш-таблиц. В более ранних версиях они фактически были реализованы с помощью наборов, но это изменило AFAIK. Единственное, что вы сохраняете, используя набор, - это размер указателя для каждой записи (указателя на значение).
Чтобы использовать только часть кортежа для хэш-кода, вам нужно создать подкласс кортежа и переопределить метод хэш-кода:
class ProteinTuple(tuple):
def __new__(cls, m1, m2, m3):
return tuple.__new__(cls, (m1, m2, m3))
def __hash__(self):
return hash(self[0])
Имейте в виду, что в этом случае вы платите за дополнительный вызов функции __hash__, потому что в противном случае это был бы метод C.
Я бы обратился к предложениям Константина, вынул идентификатор из кортежа и посмотрел, насколько это помогает.
Я предполагаю, что проблема, которую вы пытаетесь решить, сокращая объем используемой памяти, - это ограничение адресного пространства вашего процесса. Кроме того, вы ищете структуру данных, которая позволяет вам быстро вставлять и последовательно считывать данные.
Вы задаетесь вопросом, как структурировать данные в одном процессе, чтобы использовать меньше памяти. Единственный канонический ответ на этот вопрос (если вам все еще нужен ассоциативный поиск), используйте как можно меньше других структур, кроме строк Python (str, а не unicode). Хеш (словарь) python довольно эффективно хранит ссылки на ваши строки (это не реализация b-дерева).
Однако я думаю, что с этим подходом вы далеко не уйдете, поскольку вы сталкиваетесь с огромными наборами данных, которые в конечном итоге могут просто превышать адресное пространство процесса и физическую память машины, с которой вы работаете в целом.
Я бы предложил другое решение, которое не связано с изменением структуры данных на что-то, что сложнее вставить или интерпретировать.
Преимущество описываемого мною подхода состоит в том, что
Существует множество пакетов и подходов к распределенной обработке, некоторые из которых
спасибо за размещение ссылок на библиотеки распараллеливания. Я думал о том, чтобы сделать свое приложение многопроцессорным, но не хотел разбираться в сложных вещах. Возможно, я воспользуюсь одним из них, когда закончу набор инструментов.
Это все еще неясно, но похоже, что у вас есть несколько списков [(id, description, type) ...]
Идентификаторы уникальны в пределах списка и согласованы между списками.
Вы хотите создать UNION: единый список, в котором каждый идентификатор встречается один раз, возможно, с несколькими описаниями.
По какой-то причине вы думаете, что сопоставление может быть слишком большим. У вас есть доказательства этого? Не выполняйте чрезмерную оптимизацию без реальных измерений.
Это может быть (если я правильно угадаю) стандартная операция «слияния» из нескольких источников.
source1.sort()
source2.sort()
result= []
while len(source1) > 0 or len(source2) > 0:
if len(source1) == 0:
result.append( source2.pop(0) )
elif len(source2) == 0:
result.append( source1.pop(0) )
elif source1[0][0] < source2[0][0]:
result.append( source1.pop(0) )
elif source2[0][0] < source1[0][0]:
result.append( source2.pop(0) )
else:
# keys are equal
result.append( source1.pop(0) )
# check for source2, to see if the description is different.
Это объединяет два списка путем сортировки и слияния. Нет сопоставления, нет хеша.
Это интересное решение, но мне придется расширить его, чтобы можно было использовать n источников (n потенциально может быть 15 или 20), и допустить, чтобы идентификатор дважды появлялся в одном из источников. Источник может быть собран человеком, поэтому мне нужно учесть эту возможность. Короче говоря, мне придется провести некоторое время для анализа.
Если вы выполняете n-way слияние с удалением дубликатов, возможно, вы ищете следующее.
Этот генератор объединит любое количество источников. Каждый источник должен быть последовательностью. Ключ должен находиться в позиции 0. Это дает объединенную последовательность по одному элементу за раз.
def merge( *sources ):
keyPos= 0
for s in sources:
s.sort()
while any( [len(s)>0 for s in sources] ):
topEnum= enumerate([ s[0][keyPos] if len(s) > 0 else None for s in sources ])
top= [ t for t in topEnum if t[1] is not None ]
top.sort( key=lambda a:a[1] )
src, key = top[0]
#print src, key
yield sources[ src ].pop(0)
Этот генератор удаляет дубликаты из последовательности.
def unique( sequence ):
keyPos= 0
seqIter= iter(sequence)
curr= seqIter.next()
for next in seqIter:
if next[keyPos] == curr[keyPos]:
# might want to create a sub-list of matches
continue
yield curr
curr= next
yield curr
Вот сценарий, который использует эти функции для создания результирующей последовательности, которая представляет собой объединение всех источников с удаленными дубликатами.
for u in unique( merge( source1, source2, source3, ... ) ):
print u
Полный набор данных в каждой последовательности должен существовать в памяти один раз, потому что мы выполняем сортировку в памяти. Однако полученная последовательность фактически не существует в памяти. Действительно, он работает, потребляя другие последовательности.
посмотрев на ваш код, я пришел к выводу, что ваш код обрабатывает весь набор данных примерно так, как nаlg (a), где n - общее количество элементов во всех источниках, а a - количество источников. Эй, это все еще линейное время, что очень хорошо. Еще не решил, что делать, но приятный.
Вы можете ускорить процесс, отсортировав источники извне. Тогда это действительно линейно. Используйте subprocess.Popen ('sort source', stdout = PIPE) и прочтите стандартный вывод из sort.
Что вы делаете в 90% случаев с этими данными, выполняете поиск или вставку?