УЧИМСЯ АНАЛИЗИРОВАТЬ ДАННЫЕ НА ПРИМЕРЕ API CODEWARS
ВВЕДЕНИЕ
Для начинающего специалиста, входящего в мир ИТ, одной из приоритетных задач трудоустройства является отработка и получение навыков хотя бы в одном (а лучше нескольких) языках программирования.
Мой опыт через решение алгоритмических задач показал, что этот метод помогает развить системное мышление. В жизни и в работе данный навык очень важен, поскольку существуют задачи, требующие нестандартного подхода к их решению.
В настоящее время существует достаточно много проектов, помогающих развить системное мышление. В данной публикации у меня нет намерения рассказывать о каждом подробно. Обзору таких проектов посвящено множество статей, поэтому ограничусь кратким описанием наиболее популярных решений, помогающие развивать системное и алгоритмическое мышление через программирование. Их описание представлено в таблице ниже.
№ | Платформа | url | Описание |
1 | Codewars | www.codewars.com | Популярная в СНГ платформа Доступно 60 языков программирования и более 5 000 задач Задачи разделены по уровню сложности (1 — самый сложный, 8 — самый простой) |
2 | CodeForces | www.codeforces.com | Платформа по проведению соревнований Примерно раз в неделю проводятся двухчасовые соревнования Проведение тематических олимпиад |
3 | Project Euler | www.projecteuler.net | Сайт с коллекцией задач разной сложности Доступно 760 различных задач по математике, информатике, геометрии |
4 | LeetCode | www.leetcode.com | Сайт по спортивному программированию 1900 задач. 3 уровня сложности. Ориентированы на развитие навыков по алгоритмам и структурам данных. |
5 | Kaggle | www.kaggle.com | Представлены задачи, связанные с машинным обучением и нейронными сетями. Доступно решение на 2-ух языках программирования. Публикация наборов данных с целью их дальнейшего анализа. |
Отмеченные выше приложения были популярны и в 2020-ом году, когда у меня появилось желание изучить первый язык программирования. В качестве тренажёра для отработки навыка выбран CodeWars.
Платформа мне понравилась большим обилием задач (как алгоритмических, так и практических), геймификацией (идея тренироваться по методикам из карате очень зашла) и достаточно гибким поиском задач по нужной мне тематике. Также порадовало наличие системы автоматической проверки и возможность просмотра решений других участников. Вишенка на торте — предоставление статистической информации по задачам. Стоит отметить, в бесплатной версии CodeWars она достаточно урезанная, но на мой взгляд, даёт небольшое представление о текущем уровне пользователя. Например, на скриншоте ниже показана моя статистика.
Описание статистических параметров представлено в таблице ниже. Также здесь можно увидеть информацию об участии пользователя в разработке задач и количестве задач, где указанный пользователь является автором. У меня опыта разработки задач не было, поэтому эти данные в таблицу не вошли.
Подробное описание параметров описано в документации (https://docs.codewars.com/), про принципы геймификации хорошо рассказывает раздел (https://docs.codewars.com/gamification). В нём же можно узнать о правилах игры в программирование на платформе CodeWars.
№ | Параметр | Критерии | Описание |
1 | Пользовательские | Ранг (Rank) | Показатель вклада пользователя в проект |
Баллы чести (Honor) | Количество баллов активности пользователя в проекте | ||
Позиция в рейтинге | Текущая позиция среди других пользователей платформы | ||
Позиция в рейтинге (%) | Та же самая позиция, только в процентном выражении | ||
Распределение баллов чести (Honor BreakDown) | Распределение выводится в виде столбчатой диаграммы и показывает вклад пользователя в платформу. | ||
2 | Языки | Распределение рангов по языкам | Распределение в виде круговой диаграммы, показывающая вклад пользователя по решённым задачам |
За период активного пользования сервисом мной было решено порядка 811 задач разного уровня сложности от очень простого (8 уровень) до выше среднего (3 уровень), что составляет примерно 10% от всей выборки. К слову, общее количество задач на платформе по состоянию на «сегодня» составляет порядком 7700.
ОБ API CODEWARS
С течением времени мне хотелось узнать больше информации о решаемых задачах. И очень долго не доходили руки, чтобы написать приложение, на основе которого можно анализировать данные по решённым задачам.
К счастью, у платформы CodeWars есть весьма скромный API (https://dev.codewars.com/#introduction), через который можно получить немного больше статистической информации, ежели это видно пользователю на платформе. Стоит также упомянуть, что на сайте указано, что клиентский API минимальный и противоречивый и достаточно плохо разрабатывается. Но думаю, в качестве кейса по получению и обработке данных он подойдёт. Преимуществом API является то, что он доступен любому пользователю абсолютно бесплатно и даже не нужно регистрироваться в системе для извлечения данных.
Форматом извлечения данных является традиционный знакомый многим разработчикам JSON файл. В своей структуре API Codewars реализует 3 базовых запроса
1. Получение данных пользователя. Здесь выводится вся статистическая информация о пользователе, его ранге, задачах, которые он решил, языках программирования, которые он применял в своём решении. Получить эти данные можно по адресу https://www.codewars.com/api/v1/users/{user}, где в фигурных скобках нужно вставить логин пользователя
2. Получение информации о задачах. Здесь содержится информация о всех задачах, решенных пользователем на платформе. Формат запроса содержит пользовательские данные и номер страницы. Стоит также отметить, что платформа выдаёт 200 задач на одну страницу. Формат вывода: https://www.codewars.com/api/v1/users/{user}/code-challenges/completed? page={page}. Здесь также в фигурных скобках указывается имя пользователя и номер страницы
3. Получение информации об участие пользователя в разработке задачи. Представлен с помощью URL запроса: https://www.codewars.com/api/v1/users/{user}/code-challenges/authored, где {user} — имя пользователя, зарегистрированного в системе codewars
Используя эту информацию, можно реализовать получение данных с сервиса CodeWars для дальнейшего анализа и их изучения. Поскольку я не являлся активным автором задач, 3 вопрос мной досконально не изучался, но если потребуется, можно модифицировать предложенное решение и эти данные учесть при дальнейшем анализе
ПОЛУЧЕНИЕ ДАННЫХ
Получение данных осуществлялось с помощью языка программирования Python через библиотеку requests. Таким образом получить данные можно по следующему скрипту
import requests
import json
username = 'MyUserName' # здесь ваш логин CodeWars
file_name = 'my_data'+'.json' # название файла (в формате json)
def get_data_from_codewars(username, filename_jsnon: json):
data = {}
page_number = 0
while requests.get(
f'https://www.codewars.com/api/v1/users/{username}/code-challenges/completed?page={page_number}').status_code == 200:
q = requests.get(
f'https://www.codewars.com/api/v1/users/{username}/code-challenges/completed?page={page_number}')
result = q.json()
if not result['data']:
break
for j in result['data']:
j_id = j['id']
print(f'Получен {j_id}')
data[j_id] = {}
data[j_id]['completedAt'] = j['completedAt']
data[j_id]['completedLanguages'] = j['completedLanguages']
chal = requests.get(f'https://www.codewars.com/api/v1/code-challenges/{j_id}')
result_chal = chal.json()
data[j_id]['rank'] = abs(result_chal['rank']['id'])
data[j_id]['url'] = result_chal['url']
data[j_id]['totalAttempts'] = result_chal['totalAttempts']
data[j_id]['totalCompleted'] = result_chal['totalCompleted']
data[j_id]['totalStars'] = result_chal['totalStars']
data[j_id]['voteScore'] = result_chal['voteScore']
data[j_id]['tags'] = result_chal['tags']
page_number += 1
print(f'Обработано(ы) {page_number} страниц(а/ы)')
with open(filename_jsnon, "w") as outfile:
json.dump(data, outfile)
Предложенная функция извлекает данные пользователя и ассоциирует их с задачей, которую он решал в течение всего периода активности на платформе. Таким образом из всего набора параметров из запроса API наибольший интерес представили следующие поля.
Для пользователя: дата отправки решения, языки программирования, url-адрес
Для задачи: количество попыток, количество правильных решений, рейтинг (количество звёзд, количество проголосовавших), теги (соответствуют тематике решаемой задачи), уровень сложности задачи
Анализ данных осуществлялся преимущественно методами библиотеки pandas. также были дополнительно задействованы библиотеки по визуализации matplotlib, WordCloud и sklearn (использовался для нормализации)
Далее импортируем оставшиеся библиотеки и формируем DataFrame.
import matplotlib.pyplot as plt
from wordcloud import WordCloud
from sklearn import preprocessing
df = pd.read_json(r'%s' % (file_name)).T
df.info()
У нас получился dataframe из 8 полей. Ниже представлена выборка из 5 первых строк
ПРЕОБРАЗОВАНИЕ ДАННЫХ
Если внимательно посмотреть на набор данных, то можно заметить, что некоторые поля содержат данные типа list и дату окончания, не соответствующую формату. Таким образом требуется преобразовать данные с датой и временем к соответствующему ему формату и распаковать данные в списке. Для анализа выборки мне не нужна дата с точностью до дня, достаточно знать месяц и год отправки решения задачи на CodeWars.
df['completedAt'] = pd.to_datetime(df['completedAt'])
df['completedAt'] = df['completedAt'].apply(lambda x: x.strftime('%Y-%b'))
df_compLanguages = df.explode('completedLanguages')
full_df = df_compLanguages.explode('tags')
Таким образом получена таблица, которая использовалась для получения дополнительной информации по решаемым задачам
ПРИСТУПАЕМ К АНАЛИЗУ ДАННЫХ
По каким тегам решено наибольшее количество задач?
full_df['tags'].value_counts().to_frame().head(10)
full_df['tags'].value_counts().to_frame().head(10).plot.bar()
Данный запрос направлен на извлечение наиболее часто используемых тегов в выборке, как можно заметить наибольшее количество заданий направлено на освоение фундаментальных навыков, далее строки, массивы, алгоритмы, математика, списки и далее по списку.
Кстати, те же самые данные можно представить с помощью облака тегов.
tags = ' '.join(i for i in full_df['tags'].to_list())
wordcloud = WordCloud().generate(tags)
plt.figure(figsize=(10,6))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis("off")
plt.show()
Какая самая сложная задача в выборке?
df[df['rank']==df['rank'].min()]['url']
Так, а этот вопрос с подвохом. Когда я решал задачу, она была уровня 3, однако выборка показала уровень 4. Не зря же в API авторы писали, что она не совершенно. Для подтверждения скрин из статистики решений по задачам
Да и вообще с течением времени эта задача ушла на пенсию. Даже сомневаюсь, что её сейчас можно ли решать или нет
В какое время я наиболее активно решал задачи?
full_df['completedAt'].value_counts().head(10).plot.bar()
Для меня это ответ на вопрос, когда период решения задач был наиболее активным. Как примерно в это время занимался активной тренировкой навыков системного мышления
Как меняется статистика задач на каждом уровне сложности?
normalized_arr = preprocessing.normalize(df[['totalAttempts', 'totalCompleted', 'totalStars', 'voteScore']])
df[['totalAttempts', 'totalCompleted', 'totalStars', 'voteScore']] = normalized_arr
rating = df[['rank', 'totalAttempts', 'totalCompleted', 'totalStars', 'voteScore']].groupby('rank').mean().reset_index()
fig, axs = plt.subplots(2,2)
a = list(rating.columns)[1:]
for i in range(2):
for j in range(2):
axs[i,j].plot(rating['rank'],rating[a[0]])
axs[i,j].legend([a[0]])
del a[0]
Для ответа на этот вопрос потребовалась нормализация данных, поскольку в статистика по данным из задач имеют разную размерность. По графикам отчётливо видна зависимость количества попыток от количества успешно выполненных задач. Также видна зависимость рейтинга от количество удачных попыток.
Кстати, данную гипотезу подтверждает расчёт коэффициента корреляции. По ней видно, что количество успешно завершивших задачу обратно коррелируют с количеством попыток. Связь очень сильная поэтому определяем, что с увеличением количества попыток количество пользователей, завершивших задачу сокращается и наоборот. Также наблюдается логичная средняя связь между ростом числа проголосовавших с успешно завершившими задачу. То есть, чем больше решило, тем больше проголосовало.
df[['totalAttempts', 'totalCompleted', 'totalStars', 'voteScore']].corr()
plt.matshow(df[['totalAttempts', 'totalCompleted', 'totalStars', 'voteScore']].corr())
plt.show()
з
Какой язык программирования самый популярный (в %) из числа решённых задач?
lang = df_compLanguages.groupby('completedLanguages')[['completedLanguages']].count().rename(columns={'completedLanguages':'Languages'}).reset_index().sort_values(by='Languages', ascending=False)
lang['Languages'] =(100. * lang['Languages'] / lang['Languages'].sum()).round(0)
b = lang.plot.bar(x='completedLanguages', y='Languages', rot=0)
b.set_ylim(1,100)
Здесь очевидно, поскольку я был сфокусирован на тренировке языка программирования Python, то график показал Python. Кстати позже подключил SQL.
Насколько задачи сложные?
Для того, чтобы ответить на этот вопрос, нужно сначала внедрить такой показатель как коэффициент сложности. У меня он вычисляется путём деления количества попыток на успешные решения. Реализуется она по следующей формуле.
full_df['Hard'] = full_df['totalAttempts']/full_df['totalCompleted']
А ниже представлен вывод, по которым рисуется столбиковая диаграмма
hard_level = full_df.groupby(['rank'])['Hard'].mean().to_frame().reset_index()
fig, ax = plt.subplots()
ax.bar(hard_level['rank'], hard_level['Hard'])
ax.set_xlabel('Уровень')
ax.set_ylabel('Сложность')
ax.legend(['Сложность на уровень'])
По графику видно, как меняется средний уровень сложности задач. Примечательно, что сложность 5 и 6 уровня в среднем незначительная.
Топ 10 тем по уровню сложности
hardest_by_tags = full_df.groupby('tags')['Hard'].mean().reset_index().sort_values(by='Hard')
plt.bar(hardest_by_tags['tags'].head(10), hardest_by_tags['Hard'].head(10), color='green')
plt.title('Топ 10 самых лёгких тегов')
plt.xticks(rotation=90)
fig.set_size_inches(10.0, 10.5)
plt.bar(hardest_by_tags['tags'].tail(10), hardest_by_tags['Hard'].sort_values().tail(10))
plt.xticks(rotation=90)
plt.title('Топ 10 самых сложных тегов')
Данные графики наглядно показывают, что по мнению пользователей Codewars является сложной задачей, а что нет.
Как видно вот список наиболее сложных (в порядке убывания): шаблоны проектирования, производительность, ООП, игры, дата и время, парсинг, итерирование, регулярные выражения, структуры данных, алгоритмы
А вот в список самых лёгких (в порядке возрастания): Data Scince, статистика, SQL, матрицы, исправление кода, базы данных, фильтрация, алгебра, двоичные вычисления, геометрия.
Для меня лично было удивительным видеть Data Scince в числе самых лёгких задач.
Как распределяется сложность в разрезе уровней?
by_rank_hard = full_df.groupby(['rank','tags'])['Hard'].mean().to_frame().sort_values(by='Hard').sort_index()
ax = by_rank_hard.unstack(level=0).plot(kind='bar', subplots=True, rot=0, figsize=(10, 7), layout=(5,1))
plt.tight_layout()
plt.xticks(rotation=90)
plt.show()
Данный график показывает распределение задач по уровню сложности. Как видно, самые сложные задания 8 уровня по ООП, Теории чисел, дате и времени. Самые сложные на 7 уровне: производительность, учебные задачи, парсинг. Самые сложные задачи на на 6-ом уровне ООП, шаблоны проектирования. На пятом уровне сложными задачами считается раздел фундаментальных решений, ООП Регулярных выражений. В четвёртый уровень входит одна задача, поэтому на данный момент описание наиболее сложных разделов остаётся открытой задачей
Заключение
Друзья, это была моя первая попытка самостоятельного сбора и анализа данных на Python. Уверен, что, эту выборку можно использовать для постановки более сложных вопросов.
Также предложенный алгоритм можно использовать для анализа вашей статистики по решённым задачам на CodeWars, если вы там зарегистрированы.
Если для большинства читающих эту статью задача по анализу ваших данных будет актуальной, могу выложить свой анализ на гитхаб.
Благодарю всех, кто прочитал эту статью до конца. И на последок небольшой опрос