2013-03-25 3 views
2

Я пытаюсь переписать некоторые из моих старых сценариев bash, которые, как я думаю, очень неэффективны (не говоря уже о неэлегантном) и используют некоторые ужасные трубы ... Возможно, кто-то с реальными навыками Python может дать мне некоторые указатели ...python file manipulations (bash script porting)

скрипт делает использование нескольких файлов временных ... еще одна вещи, я думаю, что это плохой стиль, и, вероятно, можно избежать ...

это существенно манипулирует INPUT-FILE на первом вырезание определенного количество строк сверху (отбрасывающий заголовок).
Затем вытаскивает один из столбцов и:

  • высчитывает количество raws = N;
  • выбрасывает все повторяющиеся записи из этого единственного файла колонки (я использую sort -u -n FILE > S-FILE).

После этого я создаю индекс целых чисел от 1 до N и вставляю этот новый индексный столбец в исходный INPUT-FILE с помощью команды вставки.
Затем мой скрипт bash генерирует ранги Percentile для значений, которые мы ввели в S-FILE.
Я считаю, что Python использует scipy.stats, тогда как в bash я определяю количество повторяющихся строк (dupline) для каждой уникальной записи в S-FILE, а затем вычисляется per-rank=$((100*($counter+$dupline/2)/$length)), где $ length = length FILE, а не S-FILE. Затем я распечатывал результаты в отдельный файл с 1 столбцом (и повторял бы один и тот же за каждый ранг столько раз, сколько у нас есть).
Я бы вставил этот новый столбец с процентильными рядами обратно в INPUT-FILE (так как я хотел бы сортировать INPUT-FILE по столбцу, используемому для вычисления рангов процентиля - все будет идеально соответствовать результату).

После этого он переходит в ниже уродства ...

sort -o $INPUT-FILE $INPUT-FILE 

awk 'int($4)>2000' $INPUT-FILE | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 2000-$INPUT-FILE 

diff $INPUT-FILE 2000-$INPUT-FILE | sed '/^[0-9][0-9]*/d; s/^. //; /^---$/d' | awk 'int($4)>1000' | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 1000-$INPUT-FILE 

cat 2000-$INPUT-FILE 1000-$INPUT-FILE | sort > merge-$INPUT-FILE 

diff merge-$INPUT-FILE $INPUT-FILE | sed '/^[0-9][0-9]*/d; s/^. //; /^---$/d' | awk 'int($4)>500' | awk -v seed=$RANDOM 'BEGIN{srand(seed);} {print rand()"\t"$0}' | sort -k1 -k2 -n | cut -f2- | head -n 500 > 500-$INPUT-FILE 

rm merge-$INPUT-FILE 

По сути, это очень безвкусный Баш способ сделать следующее:

  1. СЛУЧАЙНО выбрать 500 линий от $ ВХОД -FILE, где значение в столбце 4 больше 2000 и записывает его в файл 2000- $ INPUT-FILE
  2. Для всех строк REMAINING в $ INPUT-FILE произвольно выбирайте 500 строк, где значение в столбце 4 больше 1000 и записать его в файл 1000- $ I NPUT-FILE
  3. для всех остальных строк в $ Ввод файла после 1) и 2), случайным образом выбрать 500 строк, в которых значение в колонке 4 больше, чем 500, и записывает его в файл 500- $ Ввод файла

Опять же, я надеюсь, что кто-то может помочь мне в переработке этой уродливой трубопроводной вещи в красоту питона! :) Благодаря!

+0

Вы можете добавить одну примерную строку из файла и пример того, как вы вызываете этот скрипт из командной строки? –

+0

Пожалуйста, просмотрите предварительный просмотр перед публикацией и убедитесь, что вы избегаете стенок текста. Это будет когда-либо игнорировать ваш вопрос, потому что слишком сложно расшифровать сказанное. – Serdalis

+0

Некоторые библиотеки для просмотра: 'tempfile',' difflib' 'random' – monkut

ответ

1

Два важных момента в комментариях:

(A) Файл находится ~ 50k линии ~ 100 символов. Это достаточно мало, чтобы удобно вписываться в память на современные настольные/серверные/переносные системы.

(B) Основной вопрос автора заключается в том, как отслеживать уже выбранные строки и не выбирать их снова.

Предлагаю три шага.

(1) Пройдите файл, создав три отдельных списка - назовите их u, v, w - номера строк, которые удовлетворяют каждому из критериев. Эти списки могут содержать более 500 строк, и они могут содержать дубликаты, но мы избавимся от этих проблем на этапе (2).

u = [] 
v = [] 
w = [] 

with open(filename, "r") as f: 
    for linenum, line in enumerate(f): 
     x = int(line.split()[3]) 
     if x > 2000: 
      u.append(x) 
     if x > 1000: 
      v.append(x) 
     if x > 500: 
      w.append(x) 

(2) Выберите номера строк. Вы можете использовать встроенный Random.sample(), чтобы выбрать образец из k элементов из популяции. Мы хотим удалить элементы, которые были выбраны ранее, чтобы отслеживать такие элементы в наборе. («Выбранная» коллекция представляет собой набор вместо списка, потому что тест «if x not in selected» равен O (log (n)) для набора, но O (n) для списка. Измените его на список и вы увидите замедление, если вы измеряете тайминги точно, хотя это может быть не заметна задержка для набора данных «только» 50k точек данных/500 выборок/3 категории.)

import random 
rand = random.Random()  # change to random.Random(1234) for repeatable results 

chosen = set() 
s0 = rand.sample(u, 500) 
chosen.update(s0) 
s1 = rand.sample([x for x in v if x not in chosen], 500) 
chosen.update(s1) 
s2 = rand.sample([x for x in w if x not in chosen], 500) 
chosen.update(s2) 

(3) Есть другой проходит через входной файл, помещая строки с номерами s0 в ваш первый выходной файл, строки, числа которых находятся в s1 в ваш второй выходной файл, и строки, номера которых находятся в s2 в ваш третий выходной файл. Это довольно тривиально на любом языке, но вот реализация, которая использует Python «идиомы»:

linenum2sample = dict([(x, 0) for x in s0]+[(x, 1) for x in s1]+[(x, 2) for x in s2]) 

outfile = [open("-".join(x, filename), "w") for x in ["2000", "1000", "500"]] 

try: 
    with open(filename, "r") as f: 
     for linenum, line in enumerate(f): 
      s = linenum2sample.get(linenum) 
      if s is not None: 
       outfile[s].write(line) 
finally: 
    for f in outfile: 
     f.close() 
+0

Благодарим вас за подробный ответ! пара комментариев ... 1) в третьем блоке кода join(), похоже, работает только тогда, когда я ставлю его как join ([x, filename]) 2) Я изменил размер выборки от 500 до 100 для тестирования и я получаю в результате три файла, которые, по-видимому, колеблются по размеру, т. е. сначала запускаются в большом файле с более чем 76-килограммовыми линиями, почтительно производят три файла с 98, 100 и 100 строками. второй запуск в SAME-файле производил три файла по 100, 98 и 94 строк каждый. Третий запуск показал файлы с 99, 98 и 99 строками. Я не совсем понимаю, почему ... какие-то идеи? –

+0

, по-видимому, существует возможность дублирования в s0, s1 и s2 ... и dict() не создает повторяющихся записей - то есть количество строк в выходных файлах уменьшается на количество дубликатов, найденных в каждый из наборов ... –

+0

ОК, я обошел эту проблему, используя random.sample (set (u), 500) и т. д. Спасибо! Похоже, решения справляются с небольшими исправлениями! :) –

0

разбить его на простые части.

  1. Прочтите файл csv.DictReader или csv.reader, если заголовки непригодны для использования. Когда вы повторяете строки, проверьте значение столбца 4 и вставьте строки в словарь списков, в которых словарные ключи имеют значения «gt_2000», «gt_1000», «gt_500».

  2. Итерации через ключи словаря и для каждого, создайте файл и выполните цикл 500 и для каждой итерации используйте random.randint (0, len (the_list) -1), чтобы получить случайный индекс списка , напишите его в файл, а затем удалите элемент из этого индекса из списка. Если в любом ковше может быть меньше 500 предметов, для этого потребуется немного больше.