Хранение ДНК в Swift - PullRequest
0 голосов
/ 03 мая 2020

Я собираюсь написать приложение для работы с необработанными образцами данных ДНК в виде файлов, которые вы получаете от MyHeritage, Ancestry, FamilyTreeDNA, 23 & me et c. Каждый из этих файлов в основном представляет собой CSV-файл с некоторыми причудами, и о его расшифровке задавался другой вопрос, который я опубликовал ранее.

Теперь к следующей части. Когда я проанализировал / расшифровал эти файлы, я хочу поместить данные ДНК в базу данных, чтобы я мог сравнить ДНК одного человека с ДНК другого человека. Это много данных, но не больше, чем может справиться большинство компьютеров.

В памяти я могу получить полную ДНК для обоих людей и сравнить их, а затем создать фрагменты ArraySlices для сегментов данных ДНК, которые перекрываются , но ArraySlices не подходят для хранения. В памяти ArraySlice не может существовать сам по себе. Это просто ссылка на полный массив, поэтому, если бы я сгладил ArraySlice, я бы все равно получил весь массив, даже для сегментов, которые не совпадают.

Каждый человек должен иметь свою полную ДНК в резервном хранилище и может быть прочитан в память, но как бы вы сохранили соответствующие сегменты?

Я думаю о чем-то вроде:

//Bucket is the term FamilyTreeDNA for indicating whether the DNA match is on the maternal or paternal chromosome.
enum Bucket {
    case .maternal
    case .paternal
}

//THe first 22 chromosome pairs are just numbered from 1 to 22, but the last two are X and Y, so I use a String for storing chromosome "number"

struct SharedSegment {
    let onChromosome: String
    let bucket: Bucket
    let range: Range<UInt>
}

Мне все равно, если потребуется больше диска пространство, но я хочу провести молниеносное сравнение ДНК, чтобы я мог сравнить всю ДНК для всех людей в базе данных, не занимая при этом месяцы. Также есть место для хранения полной ДНК для сравнения.

На первом этапе я просто создаю приложение для хранения наборов ДНК, которые я администрирую, но у меня уже есть планы для служб типа Gedmatch и DNAPainter, если Вы попробовали их. Это означает, что это сервис, где люди могут загружать свою ДНК для сравнения с ДНК других людей, и, скажем, миллион людей загружают свою ДНК в эту службу, и у каждого из них должна быть своя ДНК по сравнению с другими 999'999 людьми. Количество сравнений будет огромным, поэтому мой основной упор делается на производительность. Каждый файл с необработанными данными ДНК будет содержать около 400-950 тысяч строк данных ДНК.

Каждая строка будет содержать номер хромосомы, RSID, положение в хромосоме и генотип. Последнее состоит из двух букв «АА», «А C», «СТ» и т. Д. 1035 *. Есть четыре разные буквы A, C, G и T. Причина, по которой для каждой позиции есть две буквы, состоит в том, что у вас есть пары хромосом, где одна хромосома унаследована от отца, а другая - от матери, и есть одна письмо от каждой из этих двух хромосом. Конечно, я могу хранить их как просто строку символов, но есть вероятность ошибок, поэтому я хотел бы представить их в коде как

enum Aminoacid {
    noCall = 0
    case A
    case C
    case G
    case T
}

При секвенировании ДНК иногда возникают проблемы, и секвенирование оборудование не может определить, какая аминокислота находится в определенном положении. Это называется "нет вызова", поэтому дело перечисляется в перечислении noCall. в необработанном файле ДНК это обозначается как da sh, поэтому в результатах можно сказать «-A» m, что означает, что один из родителей имел A в этом положении, а другой не мог быть определен.

Есть ли возможность сжать их вместе в 4 бита (nybble), чтобы я мог хранить два из этих букв на байт? Можно даже сжать в 3 бита, но я не могу получить три буквы в байт в любом случае. Это будет две буквы по 3 бира каждая и два бита, потраченные впустую на каждый байт, так что я мог бы также использовать 4 бита на аминокислоту. В Swift есть Uint64, Uint32, Uint16 и Uint8, но нет Uint4, что было бы идеально для этого случая. Я также думаю о том, хранить ли две буквы из материнской и отцовской хромосомы вместе или же U должен разделить их на отдельный массив (один массив для материнской ДНК и один для отцовского). Существует проблема с этим подходом, и невозможно сказать, является ли первая буква в каждом ряду материнской или отцовской, пока у вас не будет ДНК хотя бы одного из родителей, с которой можно сравнить. В отсутствие их ДНК мне пришлось бы иметь третий массив для хранения обеих букв, пока я не смогу определить, какой из них является материнским и отцовским соответственно. Я пытаюсь придумать наиболее эффективный способ хранения этого, чтобы сделать сравнения очень быстрыми.

В одном смысле я не люблю использовать перечисления, и это потому, что мне придется преобразовать их Что касается rawValue, могу ли я сделать что-то вроде

var genotype = Aminoacid.A.rawValue << 4 + Aminoacid.G.rawValue

Насколько я понимаю, это лучший способ сжать два из них в один байт, так как нет UInt4.

I ' Я не очень люблю иметь много .rawValue во всем моем коде. Я хотел бы иметь только Aminoacid.A << 4 + Aminoacid.G, но, к сожалению, я не думаю, что это возможно. Может быть, есть лучший способ хранить эти последовательности аминокислот в базе данных, например, перечисления с соответствующими значениями или что-то еще. Я не знаю, насколько эффективными будут связанные значения при работе с такими большими наборами данных. </p>

Если есть кто-то, кто хочет сотрудничать в этом проекте, это пока просто проект хобби, но у меня есть планы сделать из этого бизнес в конце концов. Это означает, что я не могу нанять кого-либо для этого, но если вы работаете над подобными проектами, дайте мне знать. Мы можем сделать лучше вместе. Просто знайте, что я пишу на Swift, и я собираюсь развернуть на macOS, но Swift также доступен для других платформ, поэтому программисты для Linux и Windows одинаково приветствуются для работы над совместным проектом.

Это стало немного оффтопом c. Мой вопрос был о хранении сырой ДНК и общих сегментов таким образом, чтобы это было оптимально для быстрого поиска и сравнения огромных количеств ДНК. Я, вероятно, не буду использовать CoreData для хранения, так как я хотел бы сохранить параметры для переноса на другие платформы, чем яблоки. В настоящее время я использую CoreData, чтобы немного поэкспериментировать с хранением ДНК различными способами.

...