#ascii #julia #bioinformatics #data-conversion
#ascii #julia #биоинформатика #преобразование данных
Вопрос:
tldr: я хочу преобразовать [125, 119, 48, 126, 40]
в выходную строку, }w0~(
Чтобы привести реальный пример, я работаю с данными последовательности в формате fastq (вот ссылка на импортированную библиотеку).
cat example.fastq
выводит следующее:
@some/random/identifier
ACTAG
}w0~(
Приведенный ниже код julia демонстрирует чтение файла fastq:
import BioSequences.FASTQ
fastq_stream = FASTQ.Reader(open("example.fastq", "r"))
for record in fastq_stream
# Still need to learn, why this offset of 33?
println(
Vector{Int8}(FASTQ.quality(record, :sanger)) . 33
)
println(
String(FASTQ.sequence(record))
)
println(
String(FASTQ.identifier(record))
)
break
end
close(fastq_stream)
Этот код выводит следующее:
[125, 119, 48, 126, 40]
ACTAG
some/random/identifier
Я не хочу хранить эту информацию в списке. Я бы предпочел преобразовать его в строку. Итак, результат, который я ищу здесь, это:
}w0~(
ACTAG
some/random/identifier
Комментарии:
1.
Sanger format can encode a Phred quality score from 0 to 93 using ASCII 33 to 126
en.wikipedia.org/wiki/FASTQ_format
Ответ №1:
julia> String(UInt8.([125, 119, 48, 126, 40]))
"}w0~("
Объяснение
в Julia строки создаются с использованием набора байтов. Если вы используете только ASCII, сопоставление символов в байтах простое, и вы можете напрямую работать с необработанными данными (что также является самым быстрым способом сделать это).
Обратите внимание, что, поскольку строки Julia неизменяемы, при создании строки из необработанных байтов начальные байты становятся недоступными — это также означает, что в String
процессе создания данные не копируются. Взгляните на приведенный ниже пример:
julia> mybytes = UInt8.([125, 119, 48, 126, 40]);
julia> mystring = String(mybytes)
"}w0~("
julia> mybytes
0-element Array{UInt8,1}
Примечание по производительности
String
символы в Julia не интернализированы. В сценариях аналитики всегда рекомендуется использовать Symbol
s вместо String
s . В некоторых сценариях использование temperature=:hot
вместо temperature="hot"
может означать сокращение времени выполнения в 3 раза.
РЕДАКТИРОВАТЬ — тест производительности
julia> using Random, BenchmarkTools;Random.seed!(0);
bb = rand(33:126,1000);
julia> @btime join(Char.($bb));
31.573 μs (13 allocations: 6.56 KiB)
julia> @btime String(UInt8.($bb));
711.111 ns (2 allocations: 2.13 KiB)
String(UInt8.($bb))
работает более чем в 40 раз быстрее и использует 1/3 памяти
Комментарии:
1. Спасибо. Таким образом, полный код (в моем примере проблемы) будет
String(UInt8.(FASTQ.quality(record, :sanger) . 33))
. Примечание для пользователей библиотеки, мне сказали через проблему GitHub в репозитории, что также можно выполнить следующее, чтобы получить прямой доступ к string без такого количества преобразований:String(record.data[record.quality]
2. Интересно узнать разницу в производительности между
join(Char.([..., ...])
иString(UInt8.([..., ...])
где[..., ..]
очень длинный список целых чисел в диапазоне от 33 до 126.3.
String(UInt8.(somebytes))
более чем в 40 раз быстрее и использует 1/3 памяти — я отредактировал ответ.
Ответ №2:
На данный момент я нашел работоспособное решение. Я уверен, что существуют более эффективные решения.
join(Char(i) for i in Vector{Int8}(FASTQ.quality(record, :sanger)) . 33)
выдает требуемый мне результат.
Комментарии:
1.
join(Char.(FASTQ.quality(record, :sanger) . 33)
не использует генераторы, короче и, я бы предположил, должна быть быстрее. Я бы также предложил дать 33 имя, чтобы запомнить, что это такое.2. О, итак, судя по ссылке на комментарий, который вы добавили к вопросу, 33 означает преобразование из целых чисел Сэнгера (от 0 до 93) в целые числа ascii (от 33 до 126)? Думаю, я разобрался с этим, толком не подумав об этом. Спасибо за ссылки
3. Да, это также не произвольный выбор, 0-31 — это управляющие символы, 32 — это
Space
, от 33 до 126 — текстовые символы, 127 — этоDEL
. Вам также не нужно добавлятьVector{Int8}
в свой код, что это уже массивUInt8
, в который можно безопасно преобразоватьChar
.