У меня есть огромный файл с разделителями табуляции в таком формате
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
Я хотел бы эффективно транспонировать его, используя только команды bash (для этого я мог бы написать сценарий Perl из десяти или около того строк, но он должен выполняться медленнее, чем собственные функции bash). Итак, результат должен выглядеть так:
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
Я подумал о таком решении
cols=`head -n 1 input | wc -w`
for (( i=1; i <= $cols; i++))
do cut -f $i input | tr $'\n' $'\t' | sed -e "s/\t$/\n/g" >> output
done
Но это медленно и не кажется самым эффективным решением. Я видел решение для vi в этом посте , но оно все еще слишком медленное. Есть мысли / предложения / блестящие идеи? :-)
Ответы:
вывод
Производительность по сравнению с решением Perl от Джонатана в файле на 10000 строк
РЕДАКТИРОВАТЬ Эда Мортона (@ ghostdog74, не стесняйтесь удалять, если не одобряете).
Возможно, эта версия с более явными именами переменных поможет ответить на некоторые из приведенных ниже вопросов и в целом прояснить, что делает скрипт. Он также использует вкладки в качестве разделителя, который первоначально запрашивал OP, чтобы он обрабатывал пустые поля и по совпадению немного улучшал вывод для этого конкретного случая.
Вышеупомянутые решения будут работать на любом awk (кроме старого, сломанного awk, конечно - там YMMV).
Вышеупомянутые решения действительно читают весь файл в память - если входные файлы слишком велики для этого, вы можете сделать это:
который почти не использует память, но считывает входной файл один раз на количество полей в строке, поэтому он будет намного медленнее, чем версия, которая считывает весь файл в память. Также предполагается, что количество полей в каждой строке одинаково, и используется GNU awk для
ENDFILE
и,ARGIND
но любой awk может делать то же самое с тестами наFNR==1
иEND
.источник
Другой вариант - использовать
rs
:-c
изменяет разделитель входного столбца,-C
изменяет разделитель выходного столбца и меняет-T
местами строки и столбцы. Не используйте-t
вместо-T
, потому что он использует автоматически рассчитанное количество строк и столбцов, что обычно неверно.rs
, названный в честь функции изменения формы в APL, поставляется с BSD и OS X, но должен быть доступен в диспетчерах пакетов на других платформах.Второй вариант - использовать Ruby:
Третий вариант - использовать
jq
:jq -R .
печатает каждую строку ввода как строковый литерал JSON,-s
(--slurp
) создает массив для строк ввода после анализа каждой строки как JSON, а-r
(--raw-output
) выводит содержимое строк вместо строковых литералов JSON./
Оператор перегружен для расщепленных строк.источник
rs
- спасибо за указку! (Ссылка на Debian; апстрим выглядит как mirbsd.org/MirOS/dist/mir/rs )rs
которая поставляется с OS X,-c
одна только вкладка устанавливает разделитель входных столбцов.$'\t'
TTC TTA TTC TTC TTT
, выполнениеrs -c' ' -C' ' -T < rows.seq > cols.seq
даетrs: no memory: Cannot allocate memory
. Это система под управлением FreeBSD 11.0-RELEASE с оперативной памятью 32 ГБ. Итак, я предполагаю, что этоrs
помещает все в ОЗУ, что хорошо для скорости, но не для больших данных.Решение Python:
Вышесказанное основано на следующем:
Этот код предполагает, что в каждой строке одинаковое количество столбцов (заполнение не выполняется).
источник
l.split()
наl.strip().split()
(Python 2.7), иначе последняя строка вывода будет повреждена. Работает для произвольных разделителей столбцов, используйтеl.strip().split(sep)
и,sep.join(c)
если ваш разделитель хранится в переменнойsep
.транспонирования проект SourceForge является coreutil типа C программа именно для этой цели .
источник
-b
и-f
аргументы.Чистый BASH, без дополнительных процессов. Хорошее упражнение:
источник
printf "%s\t" "${array[$COUNTER]}"
Взгляните на файл данных GNU, который можно использовать как
datamash transpose
. В будущей версии также будет поддерживаться кросс-таблица (сводные таблицы).источник
Вот умеренно надежный Perl-скрипт для этой работы. Есть много структурных аналогий с решением @ ghostdog74
awk
.С размером данных выборки разница в производительности между perl и awk была незначительной (1 миллисекунда из семи). При большем наборе данных (матрица 100x100, элементы по 6-8 символов) perl немного превзошел awk - 0,026 с против 0,042 с. Ни то, ни другое вряд ли будет проблемой.
Типичные тайминги для Perl 5.10.1 (32-разрядная версия) vs awk (версия 20040207 при задании -V) vs gawk 3.1.7 (32-разрядная версия) на MacOS X 10.5.8 для файла, содержащего 10000 строк с 5 столбцами в каждом линия:
Обратите внимание, что gawk на этой машине намного быстрее, чем awk, но все же медленнее, чем perl. Ясно, что ваш пробег будет другим.
источник
Если вы
sc
установили, вы можете:источник
sc
имена столбцов являются одним или комбинацией двух символов. Предел есть26 + 26^2 = 702
.Для этого есть специальная утилита,
Утилита GNU datamash
Взято с этого сайта https://www.gnu.org/software/datamash/ и http://www.thelinuxrain.com/articles/transposing-rows-and-columns-3-methods
источник
Предполагая, что все ваши строки имеют одинаковое количество полей, эта программа awk решает проблему:
Проще говоря, по мере того, как вы перебираете строки, для каждого поля
f
вырастает разделенная ':' строка,col[f]
содержащая элементы этого поля. После того, как вы закончите со всеми строками, распечатайте каждую из этих строк в отдельной строке. Затем вы можете заменить нужный разделитель (например, пробел) ':', пропустив вывод черезtr ':' ' '
.Пример:
источник
Пакет данных GNU идеально подходит для решения этой проблемы, имея всего одну строку кода и потенциально произвольно большой размер файла!
источник
Хакерское решение perl может быть таким. Это приятно, потому что он не загружает весь файл в память, печатает промежуточные временные файлы, а затем использует замечательную пасту
источник
Единственное улучшение, которое я вижу в вашем собственном примере, - это использование awk, которое уменьшит количество запущенных процессов и объем данных, передаваемых между ними:
источник
Обычно я использую этот небольшой
awk
фрагмент для этого требования:Это просто загружает все данные в двумерный массив,
a[line,column]
а затем распечатывает его какa[column,line]
, чтобы транспонировать данный ввод.При этом необходимо отслеживать максимальное
max
количество столбцов в исходном файле, чтобы использовать его в качестве количества строк для обратной печати.источник
Я использовал решение fgm (спасибо fgm!), Но мне нужно было удалить символы табуляции в конце каждой строки, поэтому изменил сценарий следующим образом:
источник
Я просто искал аналогичную транпозицию bash, но с поддержкой заполнения. Вот сценарий, который я написал на основе решения fgm, которое, похоже, работает. Если это поможет ...
источник
Я искал решение для транспонирования любой матрицы (nxn или mxn) с любыми данными (числами или данными) и получил следующее решение:
источник
Если вы хотите извлечь из файла только одну (разделенную запятыми) строку $ N и превратить ее в столбец:
источник
Не очень элегантно, но эта «однострочная» команда быстро решает проблему:
Здесь cols - количество столбцов, в которых можно заменить 4 на
head -n 1 input | wc -w
.источник
Другое
awk
решение и ограниченный ввод с размером имеющейся у вас памяти.Это объединяет каждую позицию номера в поле вместе и
END
печатает результат, который будет первой строкой в первом столбце, второй строкой во втором столбце и т. Д. Будет выведено:источник
Некоторые стандартные утилиты * nix однострочные, временные файлы не нужны. NB: OP хотел эффективное исправление (то есть быстрее), и основные ответы обычно быстрее, чем этот ответ. Эти однострочные сообщения для тех, кто любит * nix по каким-либо причинам программные инструменты . В редких случаях ( например, нехватка ввода-вывода и памяти) эти фрагменты могут быть быстрее, чем некоторые из основных ответов.
Вызовите входной файл foo .
Если мы знаем, что у foo четыре столбца:
Если мы не знаем, сколько столбцов имеет foo :
xargs
имеет ограничение по размеру и, следовательно, будет неполноценной работой с длинным файлом. Какой предел размера зависит от системы, например:tr
&echo
:... или если количество столбцов неизвестно:
Использование
set
, которое нравитсяxargs
, имеет аналогичные ограничения на размер командной строки:источник
awk
.cut
,head
Не,echo
и т.д., не более POSIX совместимого кода оболочки , чемawk
сценарий - все они являются стандартными для каждой установки UNIX. Просто нет причин использовать набор инструментов, которые в сочетании требуют, чтобы вы были осторожны с содержимым вашего входного файла и каталога, из которого вы выполняете скрипт, когда вы можете просто использовать awk, и конечный результат будет быстрее и надежнее .for f in cut head xargs seq awk ; do wc -c $(which $f) ; done
Когда объем памяти слишком медленный или объем операций ввода-вывода слишком низкий, более крупные интерпретаторы ухудшают ситуацию, независимо от того, насколько хороши они были бы в более идеальных условиях. Причина № 2: awk (или почти любой другой язык) также страдает более крутой кривой обучения, чем небольшая утилита, предназначенная для того, чтобы хорошо выполнять одну задачу. Когда время выполнения дешевле, чем человеко-часы кодера, простое программирование с помощью «программных инструментов» экономит деньги.другая версия с
set
eval
источник
Другой вариант bash
Сценарий
Вывод
источник
Вот решение для Haskell. При компиляции с -O2 он работает немного быстрее, чем awk ghostdog, и немного медленнее, чем
тонко обернутый cpython Стефана на моей машине для повторяющихся строк ввода «Hello world». К сожалению, GHC не поддерживает передачу кода командной строки, насколько я могу судить, поэтому вам придется записать его в файл самостоятельно. Он усекает строки до длины самой короткой строки.источник
Решение awk, которое хранит весь массив в памяти
Но мы можем «обходить» файл столько раз, сколько потребуется выходных строк:
Что (для небольшого количества строк вывода быстрее, чем в предыдущем коде).
источник
Вот однострочник Bash, который основан на простом преобразовании каждой строки в столбец и
paste
объединении их вместе:m.txt:
создает
tmp1
файл, поэтому он не пустой.читает каждую строку и преобразует ее в столбец, используя
tr
вставляет новый столбец в
tmp1
файлкопирует результат обратно в
tmp1
.PS: Я действительно хотел использовать io-дескрипторы, но не мог заставить их работать.
источник
Один лайнер с использованием R ...
источник
Ранее я использовал два сценария для выполнения аналогичных операций. Первый находится в awk, что намного быстрее, чем второй, который находится в "чистом" bash. Возможно, вы сможете адаптировать его к своему собственному приложению.
источник