Python 3.6: ускорить замену регулярных выражений в кадре данных Pandas - PullRequest
0 голосов
/ 16 мая 2018

Я использую следующий код, чтобы прочитать 1 миллион строк данных SQL и заменить любые управляющие символы, которые могут появиться в данных, единственная проблема заключается в том, что это медленно, и это определенно «замена», которая замедляет его,У кого-нибудь есть предложения по поводу использования другого подхода или настройки, чтобы сделать код быстрее?

d = {}
x = map(chr, list(range(0,9)) + list(range(11,13)) + list(range(14,32)) + 
list(range(127,160)))
for item in list(x):
    d.update({item:' '})

with open("out_cleaned.csv", "w", encoding='utf-8') as fh:
    chunks = pd.read_sql_query(SQLCommand, connection, chunksize=10000)  
    c = next(chunks)
    c.replace(d, regex=True, inplace=True)
    c.to_csv(fh, index=False, header=False, sep='\t', chunksize=10000)  

    for chunk in chunks:
        chunk.replace(d, regex=True, inplace=True)
        chunk.to_csv(fh, index=False, header=False, sep='\t', chunksize=10000) 

Занимает 16 минут, чтобы прочитать, очистить и выписать 1 миллион строк (из 31 поля).

1 Ответ

0 голосов
/ 17 мая 2018

Для начала вам не нужно регулярное выражение - вы просто заменяете «специальные» символы пустым пробелом при замене «один к одному», но кроме этого вам вряд ли нужно анализировать и переворачивать данныедля начала в DataFrame.

Вы можете напрямую работать с подключением к БД и экспортировать столбцы, используя встроенный модуль csv, даже не углубляясь в pandas, SQLAlchemy и аналогичные тяжеловесы, которые добавляютненужные накладные расходы для вашего варианта использования.

Итак, во-первых, вместо регулярного выражения вы можете создать таблицу перевода и использовать ее с str.translate() для очистки любой строки:

chr_ranges = (0x00, 0x09), (0x0B, 0x20), (0x7F, 0xA0)  # 'special' character ranges
trans_table = {x: " " for r in chr_ranges for x in range(*r)} # 'special'->space trans. table

Это позволяет вам быстро и без усилий перевести все специальные символы, определенные в диапазонах chr_ranges, в пробел любой строки, например:

print("Your string with >\x05\x06\x1A< special characters!".translate(trans_table))
# Your string with >   < special characters!

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

def trans_field(value):
    try:
        return value.translate(trans_table)  # try to translate and return
    except AttributeError:  # if there's no translate method on the passed value...
        return value  # return the original value

Теперь все, что нам нужно, это подключиться к базе данных и выполнить наш запрос, который зависит от базы данных, которую вы используете - следующий пример я напишу какесли вы использовали SQLite, но большинство драйверов баз данных используют Python Database API и в значительной степени взаимозаменяемы, поэтому код должен работать с минимальными изменениями:

import sqlite3

connection = sqlite3.connect("your_db")  # connect to the database
cursor = connection.cursor()  # grab a database cursor
results = cursor.execute("select * from your_table")  # execute the select query
header = [c[0] for c in cursor.description]  # get the column names for our CSV header

И, наконец, мы можем выполнить итерацию порезультаты, обработайте каждое поле и сохраните все в CSV:

import csv

with open("output.csv", "w", newline="") as f:  # open("output.csv", "wb") on Python 2.x
    writer = csv.writer(f, delimiter="\t")  # create a CSV writer with \t as a delimiter
    writer.writerow(header)  # write the header (column names)
    for result in results:  # iterate over the returned results
        writer.writerow(map(trans_field, result))  # process result fields and write the row

Это исключает все ненужные преобразования и должно работать так же быстро, как Python и ваша база данных.Технически вы могли бы выжать еще большую скорость, проверив cursor.description и создав карту замены только для строк в наборе результатов (вместо попыток обработать каждое поле), но это, вероятно, не сильно увеличит общую скорость..

Итак, собрав все вместе:

import csv
import sqlite3

chr_ranges = (0x00, 0x09), (0x0B, 0x20), (0x7F, 0xA0)  # 'special' character ranges
trans_table = {x: " " for r in chr_ranges for x in range(*r)} # 'special'->space trans. table

def trans_field(value):
    try:
        return value.translate(trans_table)  # try to translate and return
    except AttributeError:  # if there's no translate method on the passed value...
        return value  # return the original value

connection = sqlite3.connect("your_db")  # connect to the database
cursor = connection.cursor()  # grab a database cursor
results = cursor.execute("select * from your_table")  # execute the select query
header = [c[0] for c in cursor.description]  # get the column names for our CSV header

with open("output.csv", "w", newline="") as f:  # open("output.csv", "wb") on Python 2.x
    writer = csv.writer(f, delimiter="\t")  # create a CSV writer with \t as a delimiter
    writer.writerow(header)  # write the header (column names)
    for result in results:  # iterate over the returned results
        writer.writerow(map(trans_field, result))  # process result fields and write the row

В качестве теста я создал таблицу 31x1M в SQLite с 22 * ​​1040 * полями (каждое заполнено 10-50случайные символы в диапазоне 0x00 - 0xA0, вкрапленные полями INTEGER и REAL, и в моей системе он очистил данные и выдал output.csv за 56 секунд.YMMV, конечно, но это определенно не должно занимать 16 минут.

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