Быстрая нормализация данных в Python / Pandas - PullRequest
0 голосов
/ 16 февраля 2020

В Python / Pandas я пытаюсь нормализовать все числовые c столбцы данных в кадрах на миллион, используя следующий код:

import pandas as pd
def normalize(df):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    numeric_cols = [col for col in df.columns if df[col].dtype in numerics]
    sums = df.sum(axis=0, skipna = True)
    for col in numeric_cols:
        df.insert(df.columns.get_loc(col)+1, col+'_norm', ((df[col]/sums[col])*1000000)+1)
    return df
dataset = pd.read_csv(csv_file, index_col=['Guide1', 'Guide2'], sep=',')
dataset = normalize(dataset)

Это короткий пример ввода:

Guide1,Guide2,Gene1,Gene2,NHT1,NHT2,hart_essential_1,hart_essential_2,lib,RPE1_n1,RPE1_n2,RPE1_n3
Gene_1-KO-3,Non-Human-Target-150-KO-26,Gene_1,Non-Human-Target-150,False,True,False,False,12426.0,10634.0,8701.0,8084.0
Gene_2-KO-3,Non-Human-Target-150-KO-26,Gene_2,Non-Human-Target-150,False,True,False,False,12300.0,12383.0,6252.0,5388.0
Gene_1-KO-3,Gene_4-KO-2,Gene_1,Gene_4,False,False,False,False,11685.0,10006.0,10621.0,7002.0
Gene_1-KO-3,Gene_5-KO-2,Gene_1,Gene_5,False,False,False,False,11347.0,6726.0,3927.0,3943.0
Gene_1-KO-3,Gene_6-KO-1,Gene_1,Gene_6,False,False,False,False,11250.0,12469.0,3552.0,3334.0    

Код работает как задумано и нормализует все столбцы цифр c этого кадра данных, но, похоже, работает очень медленно, также с другими методами нормализации. Я регулярно работаю с фреймами данных, которые содержат до нескольких миллионов строк. Для ~ 1 миллиона строк и только для 4 числовых столбцов c на моем мобильном компьютере с процессором Intel Core i7-6600U @ 2,6 ГГц нормализация занимает почти 10 минут, хотя я предварительно вычисляю суммы для каждого столбца. Есть ли способ ускорить это, например, распараллеливание или какой-нибудь изящный трюк Pandas?

Любая помощь и / или указатели очень ценятся!

Ответы [ 3 ]

0 голосов
/ 16 февраля 2020

Нормализация в этой адаптации вашего кода происходит практически мгновенно. Большую часть времени тратят на генерацию этих случайных чисел ...

import pandas as pd
import random

numeric_dtypes = {'int16', 'int32', 'int64', 'float16', 'float32', 'float64'}

# Generate data...
n = 1000000
floats = [random.uniform(0, 100000) for x in range(n)]
ints = [int(i) for i in floats]
df = pd.DataFrame()
for dtype in numeric_dtypes:
    df[dtype] = pd.Series((ints if 'int' in dtype else floats), dtype=dtype)

# Normalize...
sums = df.sum(axis=0, skipna=True)
for col in df.columns:
    if str(df[col].dtype) not in numeric_dtypes:
        continue
    df[col + "_norm"] = df[col] / sums[col] * 1000000 + 1

print(df)
0 голосов
/ 16 февраля 2020

Спасибо, что нашли время ответить мне! Я нашел другое решение, которым хотел бы поделиться. YaoYao вдохновил меня на тестирование многопроцессорной библиотеки для решения моей проблемы. Следующее решение работает очень быстро. Сценарий начинается с чтения файла .csv, определяет имена столбцов цифр c и вычисляет их суммы. Затем он разбивает набор данных на куски одинакового размера и отображает нормализующую функцию на каждый кусочек. Получающиеся в результате нормализованные кадры данных объединяются обратно, чтобы получить весь нормализованный набор данных. Я ожидал бы ускорение, возможно, на 60%, потому что я разделил данные на 3 куска. Но это буквально работает мгновенно. Если бы кто-нибудь мог прокомментировать и дать некоторое представление здесь, я был бы более чем счастлив!

0 голосов
/ 16 февраля 2020

Что касается распараллеливания, вы можете использовать ThreadPoolExecutor или ProcessPoolExecutor: вот код для одновременной обработки с использованием Threads:

from concurrent import futures

def normalize(df):
    numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
    numeric_cols = [col for col in df.columns if df[col].dtype in numerics]
    sums = df.sum(axis=0, skipna=True)

    executor = futures.ThreadPoolExecutor()
    for col in numeric_cols:
        # submit the work to Thread pool
        executor.submit(df.insert(df.columns.get_loc(
            col)+1, col+"_norm", ((df[col]/sums[col])*1000000)+1))

    # wait for all processes to finish
    executor.shutdown()
    return df


dataset = pd.read_csv(csv_file)
dataset = normalize(dataset)

Надеюсь, он вам поможет.

...