Для базовых c математических вычислений для очень больших CSV-файлов, как я могу сделать это быстрее, когда я смешал типы данных в моем CSV - с python - PullRequest
1 голос
/ 21 марта 2020

У меня есть несколько очень больших файлов CSV (+ 15Gb), которые содержат 4 начальных строки метаданных / информации заголовка, а затем данные. Первые 3 столбца являются трехмерными декартовыми координатами и являются значениями, которые мне нужно изменить с помощью основных математических операций. например, сложение, вычитание, умножение, деление. Мне нужно сделать это по массе для каждого из столбцов координат. Первые 3 столбца являются значениями типа с плавающей запятой

Остальные столбцы в CSV могут быть любого типа, например, string, int, et c ....

В настоящее время я использую скрипт, в котором я могу прочитать в каждой строке CSV и внести изменения, а затем записать в новый файл, и, кажется, работает нормально. Но проблема в том, что на большие файлы уходят дни. Машина, на которой я работаю, имеет много памяти (120 Гб), но в текущем методе это не используется.

Я знаю, что могу обновить столбец по массе, используя массив numpy 2D, если пропущу 4 строки метаданных. Например,

arr = np.genfromtxt(input_file_path, delimiter=',', skip_header=4)
arr[:,0]=np.add(arr[:,0],300)

. Это обновит первый столбец, добавив 300 к каждому значению. Но проблема с попыткой использования numpy состоит в том, что

  1. Numpy массивы не поддерживают смешанные типы данных для остальных столбцов, которые будут импортированы (я не Я не знаю, что будут содержать другие столбцы, поэтому я не могу использовать структурированные массивы - или, скорее, я хочу, чтобы это был универсальный инструмент, поэтому мне не нужно знать, что они будут содержать)

  2. Я могу экспортировать массив numpy в csv (при условии, что это не смешанные типы), и просто используя обычные текстовые функции, я могу создать отдельный CSV для 4 строк метаданных, но затем мне нужно как-то объединить их, и я не Я не хочу читать все строки данных CSV, просто чтобы добавить их в конец метаданных CSV.

Я знаю, смогу ли я сделать эту работу с Numpy it значительно увеличит скорость, используя большой объем памяти машины, удерживая весь CSV в памяти во время выполнения операций. Я никогда не использовал pandas, но также рассмотрел бы использование его для решения. Я немного заглянул в pandas, думая, что, возможно, смогу сделать это с кадрами данных, но мне все еще нужно выяснить, как иметь 4 строки в качестве заголовка моего столбца вместо одного, и, кроме того, я не видел способа применить массовое обновление ко всему столбцу (как я могу с numpy) без использования python l oop - не уверен, замедлит ли это или нет, если он уже находится в памяти.

Image of potential data

Метаданные могут быть пустыми для строк 2,3,4, но в большинстве случаев в строке 4 будет записан тип данных. В дополнение к начальным 3 столбцам координат может быть до 200 столбцов данных.

Мой текущий (медленный) код выглядит следующим образом:

import os
import subprocess
import csv
import numpy as np


def move_txt_coords_to(move_by_coords, input_file_path, output_file_path):

    # create new empty output file
    open(output_file_path, 'a').close()

    with open(input_file_path, newline='') as f:
        reader = csv.reader(f)
        for idx, row in enumerate(reader):
            if idx < 4:
                append_row(output_file_path, row)
            else:
                new_x = round(float(row[0]) + move_by_coords['x'], 3)
                new_y = round(float(row[1]) + move_by_coords['y'], 3)
                new_z = round(float(row[2]) + move_by_coords['z'], 3)
                row[0] = new_x
                row[1] = new_y
                row[2] = new_z
                append_row(output_file_path, row)


def append_row(output_file, row):
    f = open(output_file, 'a', newline='')
    writer = csv.writer(f, delimiter=',')
    writer.writerow(row)
    f.close()


if __name__ == '__main__':
    move_by_coords = {
        'x': -338802.5,
        'y': -1714752.5,
        'z': 0
    }

    input_file_path = r'D:\incoming_data\large_data_set1.csv'
    output_file_path = r'D:\outgoing_data\large_data_set_relocated.csv'
    move_txt_coords_to(move_by_coords, input_file_path, output_file_path)

1 Ответ

1 голос
/ 21 марта 2020

Хорошо, я получил почти полный ответ, и это было намного проще, чем пытаться использовать numpy.

import pandas pd

    input_file_path = r'D:\input\large_data.csv'
    output_file_path = r'D:\output\large_data_relocated.csv'

    move_by_coords = {
            'x': -338802.5,
            'y': -1714752.5,
            'z': 0
        }

    df = pd.read_csv(input_file_path, header=[0,1,2,3])
    df.centroid_x += move_by_coords['x']
    df.centroid_y += move_by_coords['y']
    df.centroid_z += move_by_coords['z']

    df.to_csv(output_file_path,sep=',')

Но у меня осталась одна проблема (возможно, 2). Пустые ячейки в моем заголовке заполняются Безымянным. Мне как-то нужно вставить пустую строку для тех, кто находится в строке заголовка.

enter image description here

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

--------------------- Обновление -------- ----- Хорошо, я решил проблему с многострочным заголовком. Я просто использую обычный модуль чтения csv, чтобы прочитать первые 4 строки в список строк, а затем перенести это в список столбцов, где я одновременно преобразую список столбцов в кортежи. Как только у меня будет список кортежей заголовков столбцов (где они состоят из каждой строки в заголовке этого столбца), я могу использовать этот список для именования заголовка. Поэтому я пропускаю строки заголовков при чтении csv во фрейм данных, а затем обновляю каждый столбец по его индексу. Я также опускаю столбец индекса при экспорте обратно в CSV, как только это будет сделано. Кажется, работает очень хорошо.

import csv
import itertools
import pandas as pd


def make_first_4rows_list_of_tuples(input_csv_file_path):
    f = open(input_csv_file_path, newline='')
    reader = csv.reader(f)
    header_rows = []
    for row in itertools.islice(reader, 0, 4):
        header_rows.append(row)

    header_col_tuples = list(map(tuple, zip(*header_rows)))
    print("Header columns: \n", header_col_tuples)
    return header_col_tuples


if __name__ == '__main__':
    move_by_coords = {
        'x': 1695381.5,
        'y': 5376792.5,
        'z': 100
    }

    input_file_path = r'D:\temp\mydata.csv'
    output_file_path = r'D:\temp\my_updated_data.csv'

    column_headers = make_first_4rows_list_of_tuples(input_file_path)
    df = pd.read_csv(input_file_path, skiprows=4, names=column_headers)
    df.iloc[:, 0] += move_by_coords['x']
    df.iloc[:, 1] += move_by_coords['y']
    df.iloc[:, 2] += move_by_coords['z']
    df.to_csv(output_file_path, sep=',', index=False)

updated and exported csv

...