Как в Julia преобразовать список десятичных знаков ASCII в строку?

#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 .