#ruby #arrays
#ruby #массивы
Вопрос:
Я очищаю большие файлы данных ( 1 мм строк, разделенных запятыми). Пример строки может выглядеть так:
@row = "123456789,11122,CustomerName,2014-01-31,2014-02-01,RemoveThisEntry,R,SKUInfo,05-MAR-14 05:50:24,SourceID,RemoveThisEntryToo,TransactionalID"
Из него должны быть удалены определенные столбцы, после чего строка должна выглядеть так:
@row = "123456789,11122,CustomerName,2014-01-31,2014-02-01,R,SKUInfo,05-MAR-14 05:50:24,SourceID,TransactionalID"
ВОПРОС 1: Если я преобразую строку данных в an Array
, какой метод предпочтительнее для удаления элементов: Array#delete_at или Array#slice !? Я хотел бы знать, какой вариант является более идиоматичным. Здесь важна производительность, и я нахожусь на компьютере с Windows.
def remove_bad_columns
ary = @row.split(",")
ary.delete_at(10)
ary.delete_at(5)
@row = ary.join(",")
end
ВОПРОС 2: Мне было интересно, был ли реализован один из этих методов с использованием другого. Как я могу увидеть, как методы встроены в ruby? (Как for
это реализовано с помощью each
, например.)
Комментарии:
1. Вы должны использовать
CSV
библиотеку Ruby при обработке данных CSV. Если важна скорость, инструменты командной строки обычно быстрее:cut -d "," -f 1-5,7-10,12 largedatafile.csv
2. Производительность здесь имеет значение, так как файлы могут содержать 6 ММ строк при просмотре наблюдений за год (или более). (Я должен был упомянуть об этом в вопросе.) Я тоже пытался использовать
CSV
библиотеку на первом проходе (потому что вы определенно правы), но скрипт продолжал натыкаться на неприятные артефакты в данных — после стольких изменений с помощью манипуляций со строками я решил, что просто попытаюсь придерживаться строк повсюду. Я не рассматривал такую утилиту, как * nixcut
, и сейчас скачиваюcygwin
, чтобы попробовать. Спасибо!3. Вы всегда можете покопаться в коде MRI Ruby. Он открыт, и это позволит вам увидеть, как все реализовано на более низком уровне. Будьте готовы прочитать C, хотя 🙂 github.com/ruby/ruby
4. В MRI Ruby
Array#slice!
это реализуется функциейrb_ary_slice_bang
, которая в случае, если вы передаете только один индекс для удаления, просто вызываетrb_ary_delete_at
.rb_ary_delete_at
это функция, которая в конечномArray#delete_at
итоге тоже реализуется, с помощьюrb_delete_at_m
которой просто преобразует параметр из объекта Ruby в C ‘long’.5. Если ваши столбцы содержат встроенные запятые, вас быстро загонят в угол, если вы попытаетесь разделить, используя что-то такое простое, как
split(',')
. CSV будет обрабатывать условия, которые простой код не может.
Ответ №1:
Я предлагаю вам использовать Array#values_at, а не delete_at
or slice!
:
def remove_vals(str, *indices)
ary = str.split(",")
v = (0...ary.size).to_a - indices
ary.values_at(*v).join(",")
end
@row = "123456789,11122,CustomerName,2014-01-31,2014-02-01,RemoveThisEntry,"
"R,SKUInfo,05-MAR-14 05:50:24,SourceID,RemoveThisEntryToo,TransactionalID"
@row = remove_vals(@row, 5, 10)
#=> "123456789,11122,CustomerName,2014-01-31,2014-02-01,R,SKUInfo,"
# "05-MAR-14 05:50:24,SourceID,TransactionalID"
Array#values_at
имеет преимущество перед двумя другими методами в том, что вам не нужно беспокоиться о порядке удаления элементов.
Эффективность этого метода существенно не отличается от двух других. Если @spickermann хотел бы добавить его в свои тесты, он мог бы использовать это:
def values_at
ary = array.split(",")
v = (0...ary.size).to_a - [5,10]
@row = ary.values_at(*v).join(",")
end
Комментарии:
1. Это потрясающая стратегия, и она сработала как gangbusters на небольшом тесте — спасибо!
values_at
В мой список ЗАДАЧ добавлено использование…
Ответ №2:
На самом деле разницы в производительности нет. Я бы предпочел delete_at
, потому что это читается лучше.
require 'benchmark'
def array
"123456789,11122,CustomerName,2014-01-31,2014-02-01,RemoveThisEntry,R,SKUInfo,05-MAR-14 05:50:24,SourceID,RemoveThisEntryToo,TransactionalID"
end
def delete_at
ary = array.dup.split(",")
ary.delete_at(10)
ary.delete_at(5)
@row = ary.join(",")
end
def slice!
ary = array.dup.split(",")
ary.slice!(10)
ary.slice!(5)
@row = ary.join(",")
end
require 'benchmark'
n = 1_000_000
Benchmark.bmbm(15) do |x|
x.report("delete_at :") { n.times do; delete_at; end }
x.report("slice! :") { n.times do; slice! ; end }
end
# Rehearsal ---------------------------------------------------
# delete_at : 4.560000 0.000000 4.560000 ( 4.566496)
# slice! : 4.580000 0.010000 4.590000 ( 4.576767)
# ------------------------------------------ total: 9.150000sec
#
# user system total real
# delete_at : 4.500000 0.000000 4.500000 ( 4.505638)
# slice! : 4.600000 0.000000 4.600000 ( 4.613447)
Комментарии:
1. Я тоже выбрал
delete_at
для удобства чтения, ха-ха. Спасибо вам за то, что вы также выполнили тестовую проверку — как вы думаете, означает ли их сходство, что одно реализовано в терминах другого?2. Метода нет
deleted_at
.