Повышение эффективности кода при генерации набора данных - PullRequest
0 голосов
/ 04 февраля 2020

Я пытаюсь сгенерировать набор данных, в котором каждый день в заданном диапазоне лет имеет фиксированное количество магазинов. В свою очередь, каждый магазин продает фиксированное количество товаров. Товары, указанные c для каждого магазина и дня, имеют значение для продаж (£) и количества проданных товаров.

Однако запуск этих циклов for требует времени для создания набора данных.

Могу ли я улучшить эффективность моего кода?

# Generate one row Dataframes (for concatenation) for each product, in each store, on each date

dataframes = []
for d in datelist:
    for s in store_IDs:
        for p in product_IDs:
            products_sold = random.randint(1,101)
            sales = random.randint(100,1001)
            data_dict = {'Date': [d], 'Store ID': [s], 'Product ID': [p], 'Sales': [sales], 'Number of Products Sold': [products_sold]}
            dataframe = pd.DataFrame(data_dict) 
            dataframes.append(dataframe)

test_dataframe = pd.concat(dataframes)

Ответы [ 2 ]

1 голос
/ 04 февраля 2020

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

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

import pandas as pd
import random

datelist = [1, 2, 4, 55]
store_IDs = ['6A', '27B', '12C']
product_IDs = ['soap', 'gum']


data = []  # I just renamed this for clarity
for d in datelist:
    for s in store_IDs:
        for p in product_IDs:
            products_sold = random.randint(1,101)
            sales = random.randint(100,1001)
            data_dict = {'Date': d, 'Store ID': s, 'Product ID': p, 'Sales': sales, 'Number of Products Sold': products_sold}
            data.append(data_dict)  # this is building a list of dictionaries...

print(data[:3])

df = pd.DataFrame(data)
print(df.head())

Выход:

[{'Date': 1, 'Store ID': '6A', 'Product ID': 'soap', 'Sales': 310, 'Number of Products Sold': 35}, {'Date': 1, 'Store ID': '6A', 'Product ID': 'gum', 'Sales': 149, 'Number of Products Sold': 34}, {'Date': 1, 'Store ID': '27B', 'Product ID': 'soap', 'Sales': 332, 'Number of Products Sold': 60}]

   Date Store ID Product ID  Sales  Number of Products Sold
0     1       6A       soap    310                       35
1     1       6A        gum    149                       34
2     1      27B       soap    332                       60
3     1      27B        gum    698                       21
4     1      12C       soap    658                       51
[Finished in 0.6s]
0 голосов
/ 04 февраля 2020

Вы понимаете, что ваши размеры огромны?

Размер составляет примерно 3 с половиной года (в днях) = 1277, умноженный на 99 магазинов = 126 423, умноженный на 8999 товаров = 1 137 680 577 строк.

Если вам нужно в среднем 16 байтов (что уже немного), вам нужно как минимум 17 ГБ памяти для этого!

По этой причине Store_IDs и Product_IDs должны действительно быть просто маленькими целыми числами, как индекс в таблице с более описательными именами.

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

Предполагая, что все используемые числа могут уместиться в 16 бит, вот одно решение вашей проблемы (все еще требующее много памяти):

import pandas as pd
import numpy as np

def gen_data(datelist, store_IDs, product_IDs):
    date16 = np.arange(len(datelist), dtype=np.int16)
    store16 = np.arange(len(store_IDs), dtype=np.int16)
    product16 = np.arange(len(product_IDs), dtype=np.int16)
    A = np.array(np.meshgrid(date16, store16, product16), dtype=np.int16).reshape(3,-1)
    length = A.shape[1]
    sales = np.random.randint(100, 1001, size=(1,length), dtype=np.int16)
    sold = np.random.randint(1, 101, size=(1,length), dtype=np.int16)
    data = np.concatenate((A,sales,sold), axis=0)
    df = pd.DataFrame(data.T, columns=['Date index', 'Store ID index', 'Product ID index', 'Sales', 'Number of Products Sold'], dtype=np.int16)
    return df

FWIW на моей машине я получаю:

            Date  Store ID  Product ID  Sales  Number of Products Sold
0              0         0           0    127                       85
1              0         0           1    292                       37
2              0         0           2    180                       36
3              0         0           3    558                       88
4              0         0           4    519                       79
...          ...       ...         ...    ...                      ...
1137680572  1276        98        8994    932                       78
1137680573  1276        98        8995    401                       47
1137680574  1276        98        8996    840                       77
1137680575  1276        98        8997    717                       91
1137680576  1276        98        8998    632                       24

[1137680577 rows x 5 columns]

real    1m16.325s
user    0m22.086s
sys 0m25.800s

(у меня недостаточно памяти и я использую swap)

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