Быстрое преобразование числовых данных в файл формата с фиксированной шириной в Python - PullRequest
3 голосов
/ 06 декабря 2009

Какой самый быстрый способ преобразования записей, содержащих только числовые данные, в фиксированные строки формата и записи их в файл в Python? Например, предположим, что record - это огромный список, состоящий из объектов с атрибутами id, x, y и wt, и нам часто приходится сбрасывать их во внешний файл. Промывка может быть выполнена с помощью следующего фрагмента:

with open(serial_fname(), "w") as f: 
    for r in records:
        f.write("%07d %11.5e %11.5e %7.5f\n" % (r.id, r.x, r.y, r.wt))

Однако мой код тратит слишком много времени на генерацию внешних файлов, оставляя слишком мало времени для выполнения того, что он должен делать между сбросами.

Изменить первоначальный вопрос:

Я столкнулся с этой проблемой при написании серверного программного обеспечения, которое отслеживает глобальный набор записей путем извлечения информации из нескольких систем «производителей» и передает любые изменения в наборе записей в «потребительские» системы в режиме реального времени или вблизи него. в режиме реального времени в предварительно обработанной форме. Многие из потребительских систем являются приложениями Matlab.

Я перечислил ниже некоторые предложения, которые я получил до сих пор (спасибо) с некоторыми комментариями:

  • Сбросить только изменения, а не весь набор данных: На самом деле я уже делаю это. Результирующие наборы изменений все еще огромны.
  • Используйте двоичный (или какой-то другой, более эффективный) формат файла: Я в значительной степени ограничен тем, что Matlab может читать достаточно эффективно, и в дополнение к этому формат должен быть независимым от платформы.
  • Использовать базу данных: На самом деле я пытаюсь обойти текущее решение для базы данных, которое считается слишком медленным и громоздким, особенно на стороне Matlab.
  • Разделение задачи на отдельные процессы: В данный момент код дампа работает в своем собственном потоке. Однако из-за GIL он все еще потребляет то же ядро. Я думаю, я мог бы переместить его в совершенно отдельный процесс.

Ответы [ 5 ]

3 голосов
/ 06 декабря 2009

Я пытался проверить, может ли numpy.savetxt немного ускорить процесс, поэтому я написал следующую симуляцию:

import sys
import numpy as np

fmt = '%7.0f %11.5e %11.5e %7.5f'
records = 10000

np.random.seed(1234)
aray = np.random.rand(records, 4)

def writ(f, aray=aray, fmt=fmt):
  fw = f.write
  for row in aray:
    fw(fmt % tuple(row))

def prin(f, aray=aray, fmt=fmt):
  for row in aray:
    print>>f, fmt % tuple(row)

def stxt(f, aray=aray, fmt=fmt):
  np.savetxt(f, aray, fmt)

nul = open('/dev/null', 'w')
def tonul(func, nul=nul):
  func(nul)

def main():
  print 'looping:'
  loop(sys.stdout, aray)
  print 'savetxt:'
  savetxt(sys.stdout, aray)

Я нашел результаты (на моем 2,4 ГГц Core Duo Macbook Pro с Mac OS X 10.5.8, Python 2.5.4 из DMG на python.org, numpy 1.4 rc1, созданным из источников) немного удивительными, но они ' вполне повторяемые, поэтому я подумал, что они могут быть интересны:

$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.writ)'
10 loops, best of 3: 101 msec per loop
$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.prin)'
10 loops, best of 3: 98.3 msec per loop
$ py25 -mtimeit -s'import ft' 'ft.tonul(ft.stxt)'
10 loops, best of 3: 104 msec per loop

так, savetxt кажется на несколько процентов медленнее , чем цикл, вызывающий write ... но старый добрый print (также в цикле) кажется на несколько процентов быстрее чем write (я полагаю, это позволяет избежать каких-либо накладных расходов). Я понимаю, что разница в 2,5% или около того не очень важна, но это не то направление, в котором я интуитивно ожидал, поэтому я решил сообщить об этом. (Кстати, использование реального файла вместо /dev/null только равномерно добавляет 6 или 7 миллисекунд, так что это ничего не меняет, так или иначе).

2 голосов
/ 06 декабря 2009

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

Ваша проблема, по-видимому, в том, что вы жуете большие объемы данных, и вы медленно форматируете данные в строки и записываете строки в файл. Вы сказали «сбросить», что означает, что вам необходимо регулярно сохранять данные.

Регулярно ли вы сохраняете все данные или только измененные данные? Если вы имеете дело с очень большим набором данных, меняете только некоторые данные и записываете все данные ... это ракурс, который мы могли бы использовать для решения вашей проблемы.

Если у вас большой набор данных, и вы хотите время от времени обновлять его ... вы являетесь кандидатом в базу данных. Реальная база данных, написанная на C для скорости, позволит вам создавать множество обновлений данных и будет поддерживать все записи в согласованном состоянии. Затем вы можете периодически запускать «отчет», который будет извлекать записи и записывать из них ваш текстовый файл фиксированной ширины.

Другими словами, я предлагаю вам разделить проблему на две части: обновить набор данных по частям по мере вычисления или получения большего количества данных и выгрузить весь набор данных в текстовый формат фиксированной ширины для дальнейшей обработки. .

Обратите внимание, что вы можете сгенерировать текстовый файл из базы данных, не останавливая процесс Python, который его обновляет. Вы получите неполный снимок, но если записи независимы, это должно быть хорошо.

Если ваша дальнейшая обработка также выполняется в Python, вы можете просто оставить данные в базе данных навсегда. Не беспокойтесь об обратном циклическом переключении данных через текстовый файл с фиксированной шириной. Я предполагаю, что вы используете текстовый файл фиксированной ширины, потому что данные легко извлечь снова для дальнейшей обработки.

Если вы используете идею базы данных, попробуйте использовать PostgreSQL. Это бесплатно и это настоящая база данных. Для использования базы данных с Python вы должны использовать ORM. Одним из лучших является SqlAlchemy.

Еще один момент, который следует учитывать: если вы сохраняете данные в формате текстового файла с фиксированной шириной для последующего анализа и использования данных в другом приложении, и если это приложение может считывать JSON, а также с фиксированной шириной, возможно, вы мог бы использовать модуль C, который пишет JSON. Это может быть не быстрее, но может; Вы могли бы сравнить его и посмотреть.

Кроме вышесказанного, моя единственная другая идея - разделить вашу программу на «рабочую» часть и «часть обновления», где рабочий генерирует обновленные записи, а часть обновления сохраняет записи на диск. Возможно, пусть они общаются, когда рабочий помещает обновленные записи в текстовом формате на стандартный вывод; и программа обновления считывает данные со стандартного ввода и обновляет свою запись данных. Вместо базы данных SQL программа обновления может использовать словарь для хранения текстовых записей; по мере поступления новых он может просто обновить словарь. Примерно так:

for line in sys.stdin:
    id = line[:7]  # fixed width: id is 7 wide
    records[id] = line # will insert or update as needed

На самом деле программа обновления может хранить два словаря и обновлять один, пока другой записывается на диск.

Разделение на работника и средство обновления - это хороший способ убедиться, что работник не тратит все свое время на обновление, и отличный способ сбалансировать работу между несколькими ядрами ЦП.

У меня пока нет идей.

0 голосов
/ 06 декабря 2009

Вы можете использовать попытку переместить ваш цикл в C, используя ctypes.

0 голосов
/ 06 декабря 2009

Теперь, когда вы обновили свой вопрос, у меня есть немного лучшее представление о том, с чем вы столкнулись.

Я не знаю, что такое «текущее решение для баз данных, которое считается слишком медленным и громоздким», но я все же думаю, что база данных поможет, если используется правильно.

Запустите код Python для сбора данных и используйте модуль ORM для вставки / обновления данных в базу данных. Затем запустите отдельный процесс для создания «отчета», который будет представлять собой текстовые файлы фиксированной ширины. База данных будет выполнять все работу по генерации вашего текстового файла. При необходимости разместите базу данных на своем собственном сервере, так как в наши дни аппаратное обеспечение довольно дешевое.

0 голосов
/ 06 декабря 2009

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

быстрее: Вы можете использовать двоичные файлы, а не текстовые файлы для регистрации информации. Но тогда вам нужно написать другой инструмент для просмотра двоичных файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...