Наука о данных

Эффективные панды: использование Chunksize для больших наборов данных

Эффективное изучение больших наборов данных с помощью Pandas

Специалисты в области науки о данных часто сталкиваются с очень большими наборами данных с сотнями измерений и миллионами наблюдений. Есть несколько способов обработки больших наборов данных. Все мы знаем о распределенных файловых системах, таких как Hadoop и Spark, для обработки больших данных путем распараллеливания между несколькими рабочими узлами в кластере. Но в этой статье мы будем использовать атрибут pandas chunksize или функцию get_ chunk ().

Представьте на секунду, что вы работаете над новым фильмом и хотите знать:

1. Какой самый распространенный рейтинг фильмов от 0,5 до 5,0?

2. Каков средний рейтинг большинства снятых фильмов.

Чтобы ответить на эти вопросы, во-первых, нам нужно найти набор данных, содержащий рейтинги фильмов для десятков тысяч фильмов. Благодарим Grouplens за предоставление набора данных Movielens, который содержит более 20 миллионов оценок фильмов от более 138 000 пользователей, охватывающих более 27 000 различных фильмов.

Это большой набор данных, который используется для построения рекомендательных систем, и это именно то, что нам нужно. Итак, давайте извлечем его с помощью wget. Я работаю в Colab, но подойдет любой ноутбук или IDE.

!wget -O moviedataset.zip https://s3-api.us-geo.objectstorage.softlayer.net/cf-courses-data/CognitiveClass/ML0101ENv3/labs/moviedataset.zip
print('unziping ...')
!unzip -o -j moviedataset.zip

При распаковке папки отображаются 4 файла CSV:

links.csv

movies.csv

rating.csv

tags.csv

Нас интересует набор данных rating.csv, который содержит более 20 миллионов оценок фильмов для более чем 27 000 фильмов.

# First let's import a few libraries
import pandas as pd
import matplotlib.pyplot as plt

Давайте взглянем на файл rating.csv

ratings_df = pd.read_csv('ratings.csv')
print(ratings_df.shape)
>>
  (22884377, 4)

Как и ожидалось, фрейм данных rating_df содержит более двадцати двух миллионов строк. Это большой объем данных, которые должна обрабатывать память нашего компьютера. Чтобы производить вычисления на этом наборе данных, эффективно обрабатывать набор данных по частям, один за другим. В некотором роде ленивым способом, используя объект-итератор.

Обратите внимание: нам не нужно читать файл целиком. Мы могли бы просто просмотреть первые пять строк, используя функцию head () следующим образом:

pd.read_csv('ratings.csv').head()

Здесь важно поговорить об итеративных объектах и ​​итераторах ...

Итерируемый объект - это объект, имеющий связанный метод iter (). После применения этого метода iter () к итерируемому объекту создается объект-итератор. Под капотом это то, что делает цикл for, он принимает итерацию, такую ​​как list, string или tuple и применяет метод iter (), создает итератор и выполняет итерацию по нему. Итерируемый объект также имеет метод __get_item __ (), который позволяет извлекать из него элементы с помощью квадратных скобок.

См. Ниже пример преобразования итерируемого объекта в объект-итератор.

# x below is a list. Which is an iterable object.
x = [1, 2, 3, 'hello', 5, 7]
# passing x to the iter() method converts it to an iterator.
y = iter(x)
# Checking type(y)
print(type(y))
>>
<class 'list_iterator'>

Объект, возвращаемый вызовом функции pd.read_csv () для файла, является итерируемым объектом. Это означает, что у него есть метод __get_item __ () и связанный с ним метод iter (). Однако передача фрейма данных методу iter () создает объект карты.

df = pd.read_csv('movies.csv').head()
# Let's pass the data frame df, to the iter() method
df1 = iter(df)
print(type(df1))
>>
<class 'map'>

Итератор определяется как объект, имеющий связанный метод next (), который производит последовательные значения.

Чтобы создать итератор из итерируемого объекта, все, что нам нужно сделать, это использовать функцию iter () и передать ему итерацию. Затем, как только у нас определен итератор, мы передаем его методу next (), и он возвращает первое значение. повторный вызов next () возвращает следующее значение и так далее ... Пока не останется больше возвращаемых значений, а затем он выдает нам StopIterationError.

x = [1, 2, 3]
x = iter(x)  # Converting to an iterator object
# Let’s call the next function on x using a for loop
for i in range(4): print(next(x))
>>
1
2
3
StopIterationError
# Error is displayed if next is called after all items have been printed out from an iterator object

Обратите внимание, что термины функция и метод используются здесь как синонимы. Обычно они означают одно и то же. Просто метод обычно применяется к объекту, подобному методу head () в фрейме данных, в то время как функция обычно принимает аргумент, такой как функция print ().

Если вы хотите узнать о Python-интерпретаторах и генераторах, прочтите эту ссылку на мою записную книжку на Github. Для этой статьи это не обязательно.

Ok. вернемся к фрейму данных rating_df. Мы хотим ответить на два вопроса:

1. Какой самый распространенный рейтинг фильмов от 0,5 до 5,0?

2. Каков средний рейтинг большинства фильмов.

Давайте проверим, сколько памяти занимает фрейм данных rating_df.

ratings_memory = ratings_df.memory_usage().sum()
# Let's print out the memory consumption
print('Total Current memory is-', ratings_memory,'Bytes.')
# Finally, let's see the memory usage of each dimension.
ratings_df.memory_usage()
>>
Total Current memory is- 732300192 Bytes.
Index              128
userId       183075016
movieId      183075016
rating       183075016
timestamp    183075016
dtype: int64

Мы видим, что общее потребление памяти этим набором данных превышает 732,3 миллиона байтов… Вау.

Поскольку нас интересуют рейтинги, давайте получим разные ключи рейтингов по шкале от 0,5 до 5,0.

# Let's get a list of the rating scale or keys
rate_keys = list(ratings_df['rating'].unique())
# let's sort the ratings keys from highest to lowest.
rate_keys = sorted(rate_keys, reverse=True) 
 
print(rate_keys)
>>
  [5.0, 4.5, 4.0, 3.5, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5]

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

Наша первая цель - подсчитать количество рейтингов фильмов на каждый ключ рейтинга. Сколько рейтингов из 22 с лишним миллионов рейтингов имеет каждая клавиша? Ответ на этот вопрос автоматически отвечает на наш первый вопрос: -

Вопрос первый:

1. Какой самый распространенный рейтинг фильмов от 0,5 до 5,0?

давайте создадим словарь, ключи которого являются уникальными ключами рейтинга, используя простой цикл for. Затем мы присваиваем каждой клавише нулевое значение.

ratings_dict = {}
for i in rate_keys: ratings_dict[i] = 0
ratings_dict
>>
{0.5: 0,  1.0: 0,  1.5: 0,  2.0: 0,  2.5: 0,  3.0: 0,  3.5: 0,  4.0: 0,  4.5: 0,  5.0: 0}

Затем мы используем функцию python enumerate (), передаем функцию pd.read_csv () в качестве первого аргумента, а затем в read_csv () , мы указываем chunksize = 1000000, чтобы читать фрагменты из миллиона строк данных за раз.

Мы начинаем индекс функции enumerate () с 1, передавая start = 1 в качестве второго аргумента . S o, что мы можем вычислить среднее количество байтов, обрабатываемых для каждого блока, используя индекс. Затем мы используем простой цикл for для ключей рейтинга и извлекаем количество оценок для каждого ключа для каждого блока и суммируем их для каждого ключа в rating_dict

Последний rating_dict будет содержать каждый ключ рейтинга в качестве ключей и общий рейтинг для каждого ключа в качестве значений.

Используя атрибут chunksize, мы можем увидеть, что:
Общее количество фрагментов: 23
Среднее количество байтов на фрагмент: 31,8 миллиона байтов

Это означает, что мы обработали около 32 миллиона байтов данных на блок по сравнению с 732 миллионами байтов, если бы мы работали сразу на полном фрейме данных. Это эффективно с точки зрения вычислений и памяти, хотя и за счет ленивых итераций фрейма данных.

Есть 23 блока, потому что мы взяли 1 миллион строк из набора данных за один раз, а есть 22,8 миллиона строк. Это означает, что 23-й блок содержит последние 0,8 миллиона строк данных.

Мы также можем увидеть наш рейтинг rating_dict ниже с каждым ключом рейтинга и общим количеством оценок для каждого ключа.

{5.0: 3358218, 4.5: 1813922, 4.0: 6265623, 3.5: 2592375, 3.0: 4783899, 2.5: 1044176, 2.0: 1603254, 1.5: 337605, 1.0: 769654, 0.5: 315651}

Обратите внимание, что при указании chunksize в read_csv возвращаемое значение будет итеративным объектом типа TextFileReader .. Указание iterator=True также вернет объект TextFileReader:

# Example of passing chunksize to read_csv
reader = pd.read_csv(’some_data.csv’, chunksize=100)
# Above code reads first 100 rows, if you run it in a loop, it reads the next 100 and so on
# Example of iterator=True. Note iterator=False by default.
reader = pd.read_csv('some_data.csv', iterator=True)
reader.get_chunk(100)
This gets the first 100 rows, running through a loop gets the next 100 rows and so on.
# Both chunksize=100 and reader.get_chunk(100) return same TextFileReader object.

Это показывает, что chunksize действует так же, как функция next () итератора в том смысле, что итератор использует функцию next () для получения своего следующего элемента, в то время как функция get_chunksize () захватывает следующее указанное количество строк данных из фрейма данных, что аналогично итератору.

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

sum(list(ratings_dict.values())) == len(ratings_df)
>>
True

Наконец, давайте ответим на вопрос первый, выбрав пару "ключ-значение" из rating_dict, которая имеет максимальное значение.

# We use the operator module to easily get the max and min values
import operator
max(ratings_dict.items(), key=operator.itemgetter(1))
>>
  (4.0, 6265623)

Мы видим, что ключ рейтинга с наивысшим значением рейтинга - 4,0 со значением 6 265 623 оценок фильмов.

Таким образом, наиболее частая оценка фильма от 0,5 до 5,0 составляет 4,0.

Представим себе график ключей и значений рейтинга от максимального до минимального.

Давайте создадим фрейм данных (rating_dict_df) из rating_dict, просто преобразовав каждое значение в список и передав rating_dict пандам Функция DataFrame (). Затем мы сортируем фрейм данных по убыванию Count.

Вопрос второй:

2. Каков средний рейтинг большинства фильмов.

Чтобы ответить на этот вопрос, нам нужно рассчитать средневзвешенное распределения.

Это просто означает, что мы умножаем каждый ключ рейтинга на количество раз, когда он был оценен, складываем их все вместе и делим на общее количество оценок.

# First we find the sum of the product of rate keys and corresponding values.
product = sum((ratings_dict_df.Rating_Keys * ratings_dict_df.Count))
# Let's divide product by total ratings.
weighted_average = product / len(ratings_df)
# Then we display the weighted-average below.
weighted_average
>>
  3.5260770044122243

Итак, чтобы ответить на второй вопрос, мы можем сказать

Средняя оценка фильма от 0,5 до 5,0 - 3,5.

Довольно обнадеживает то, что по шкале 5,0 большинство фильмов имеют оценку 4,0 и средний рейтинг 3.5… Хм, кто-нибудь думает о кинопроизводстве?

Если вы похожи на большинство людей, которых я знаю, следующий логичный вопрос: -

Эй, Лоуренс, каков шанс, что мой фильм получит хотя бы средний рейтинг?

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

Это просто означает, какой процент рейтингов фильмов содержится в каждой рейтинговой кнопке?

Давайте добавим процентный столбец к rating_dict_df, используя apply и lambda.

ratings_dict_df['Percent'] = ratings_dict_df['Count'].apply(lambda x: (x / (len(ratings_df)) * 100))
ratings_dict_df
>>

Поэтому, чтобы найти процент фильмов с рейтингом не ниже среднего (3,5), мы просто суммируем процентные доли ключей фильмов 3,5. до 5.0.

sum(ratings_dict_df[ratings_dict_df.Rating_Keys >= 3.5]['Percent'])
>>
  61.308804692389046

Выводы:

Из этих упражнений мы можем сделать вывод, что по шкале 5,0 большинство фильмов имеют рейтинг 4,0 и средний рейтинг фильмов составляет 3,5 и, наконец, более 61,3% всех произведенных фильмов имеют рейтинг не менее 3,5.

Заключение:

Мы видели, как мы можем обрабатывать большие наборы данных с помощью атрибута pandas chunksize, хотя и ленивым способом, порциями.

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

Спасибо за ваше время.

P.S См. Ссылку на блокнот для этой статьи в Github.

Ура!

Обо мне:

Лоуренс - специалист по данным в Tech Layer, увлеченный справедливым и понятным искусственным интеллектом и наукой о данных. У меня есть сертификаты Data Science Professional и Advanced Data Science Professional от IBM. Я провел несколько проектов с использованием библиотек ML и DL, я люблю кодировать свои функции как можно больше, даже когда существующих библиотек предостаточно. Наконец, я никогда не прекращаю учиться и экспериментировать, и да, у меня есть несколько сертификатов по Data Science и AI, и я написал несколько очень рекомендуемых статей.

Не стесняйтесь найти меня на: -

Github

Linkedin

Twitter