[Из песочницы] Анализ данных с использованием Python

p7egngfk08rfu5wvdilzugcnda0.png

Язык программирования Python в последнее время все чаще используется для анализа данных, как в науке, так и коммерческой сфере. Этому способствует простота языка, а также большое разнообразие открытых библиотек.


В этой статье разберем простой пример исследования и классификации данных с использованием некоторых библиотек на Python. Для исследования, нам понадобится выбрать интересующий нас набор данных (DataSet). Разнообразные наборы Dataset’ы можно скачать с сайта. DataSet обычно представляет собой файл с таблицей в формате JSON или CSV. Для демонстрации возможностей исследуем простой набор данных с информацией о наблюдениях НЛО. Наша цель будет не получить исчерпывающие ответы на главный вопрос жизни, вселенной и всего такого, а показать простоту обработки достаточно большого объема данных средствами Python. Собственно, на месте НЛО могла быть любая таблица.



И так, таблица с наблюдениями имеет следующие столбцы:


  • datetime — дата появления объекта
  • city — город в котором появился объект
  • state — штат
  • country — страна
  • duration (seconds) — время на которое появился объект в секундах
  • duration (hours/min) — время на которое появился объект в часах/минутах
  • shape — форма объекта
  • comments — коментарий
  • date posted — дата публикации
  • latitude — широта
  • longitude — долгота



Для тех, кто хочет пробовать нуля, подготовим рабочее место. У меня на домашнем ПК стоит Ubuntu, поэтому покажу для нее. Для начала нужно установить сам интерпретатор Python3 с библиотеками. В убунту подобном дистрибутиве это будет:


 sudo apt-get install python3
 sudo apt-get install python3-pip 


pip  — это система управления пакетами, которая используется для установки и управления программными пакетами, написанными на Python.  С её помощью устанавливаем библиотеки, которые будем использовать:


  • sklearn  — библиотека, алгоритмов машинного обучения, она понадобится нам в дальнейшем для классификации исследуемых данных,

  • matplotlib  — библиотека для построения графиков,

  • pandas — библиотека для обработки и анализа данных. Будем использовать для первичной обработки данных,

  • numpy — математическая библиотека с поддержкой многомерных массивов,

  • yandex-translate — библиотека для перевода текста, через yandex API (для использования нужно получить API ключ в яндексе),

  • pycountry — библиотека, которую будем использовать для преобразования кода страны в полное название страны,


Используя pip пакеты ставятся просто:


pip3 install sklearn
pip3 install matplotlib
pip3 install pandas
pip3 install numpy
pip3 install yandex-translate
pip3 install pycountry


Файл DataSet — scrubbed.csv должен лежать в рабочей директории, где создается файл программы.


Итак приступим. Подключаем модули, которые используются нашей программой. Модуль подключается с помощью инструкции:


 import <название модуля> 


Если название модуля слишком длинное, и/или не нравится по соображениям удобства или политическим убеждениямм, то с помощью ключевого слова as для него можно создать псевдоним:


 import <название модуля> as <псевдоним> 


Тогда, чтобы обратиться к определенному атрибуту, который определен в модуле


<название модуля>.<Атрибут>  


или


<псевдоним>.<Атрибут>


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


 from <Название модуля> import <Атрибут>


Подключение нужных нам модулей:


import pandas as pd
import numpy as np
import pycountry
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
from yandex_translate import YandexTranslate # Используем класс YandexTranslate из модуля yandex_translate
from yandex_translate import YandexTranslateException # Используем класс YandexTranslateException из модуля yandex_translate


Для того, что бы улучшить наглядность графиков, напишем вспомогательную функцию для генерации цветовой схемы. На входе функция принимает количество цветов, которое необходимо сгенерировать. Функция возвращает связный список с цветами.


# Генерация цветовой схемы
# Возвращает список цветов
def getColors(n):
    COLORS = []
    cm = plt.cm.get_cmap('hsv', n)
    for i in np.arange(n):
        COLORS.append(cm(i))
    return COLORS


Для перевода некоторых названий с англиского на русский язык создадим функцию translate. И да, нам понадобится интернет, чтобы воспользоваться API переводчика от Яндекс.


Функция принимает на вход аргументы:


  • string — строка, которую нужно перевести,
  • translator_obj — объект в котором реализован переводчик, если равен None, то строка не переводится.


и возвращает переведенную на русский язык строку.


def translate(string, translator_obj=None):
    if translator_class == None:
        return string
    t = translator_class.translate(string, 'en-ru')
    return t['text'][0]


Инициализация объекта переводчика должна быть в начале кода.


YANDEX_API_KEY = 'Здесь должен быть определен API ключ !!!!!'
try:
    translate_obj = YandexTranslate(YANDEX_API_KEY)
except YandexTranslateException:
    translate_obj = None


YANDEX_API_KEY  — это ключ доступа к API Yandex, его следует получить в Яндексе. Если он пустой, то объект translate_obj инициализируется значением None и перевод будет игнорироваться.


Напишем еще одну вспомогательную функцию для сортировки объектов dict.


dict — представляет собой встроенный тип Python, где данные хранятся в виде пары ключ-значения. Функция сортирует словарь по значениям в убывающем порядке и возвращает отсортированные список ключей и соответсвуюущий ему по порядку следования элементов список значений. Эта функция будет полезна при построении гистограмм.


def dict_sort(my_dict):
    keys = []
    values = []
    my_dict = sorted(my_dict.items(), key=lambda x:x[1], reverse=True)
    for k, v in my_dict:
        keys.append(k)
        values.append(v)
    return (keys,values)


Мы добрались до непосредственно данных. Для чтения файла с таблицей  используем метод read_csv модуля pd. На вход функции подаем имя csv файла, и чтобы подавить предупреждения при чтении файла, задаем параметры escapechar и low_memory.


  • escapechar — символы, которые следует игнорировать
  • low_memory — настройка обработки файла. Задаем False для считывание файла целиком, а не частями.


df = pd.read_csv('./scrubbed.csv', escapechar='`', low_memory=False)


В некоторых полях таблицы есть поля со значением None. Этот встроенный тип, обозначающий неопределенность, поэтому некоторые алгоритмы анализа могут работать некорректно с этим значением, поэтому произведем замену None на строку 'unknown' в полях таблицы. Эта процедура называется импутацией.


df = df.replace({'shape':None}, 'unknown')


Поменяем коды стран на названия на русском языке с помощью библиотеки pycountry и yandex-translate.


country_label_count = pd.value_counts(df['country'].values) # Получить из таблицы список всех меток country с их количеством
for label in list(country_label_count.keys()):
    c = pycountry.countries.get(alpha_2=str(label).upper()) # Перевести код страны в полное название
    t = translate(c.name, translate_obj) # Перевести название страны на русский язык
    df = df.replace({'country':str(label)}, t)


Переведем все названия видов объектов на небе на русский язык.


shapes_label_count = pd.value_counts(df['shape'].values)
for label in list(shapes_label_count.keys()):
    t = translate(str(label), translate_obj) # Перевести название формы объекта на русский язык
    df = df.replace({'shape':str(label)}, t)


Первичную обработку данных на этом завершаем.


Постороим график наблюдений по странам. Для построения графиков используется библиотека pyplot. Примеры построения простого графика можно найти на официальном сайте https://matplotlib.org/users/pyplot_tutorial.html. Для построения гистограммы можно использовать метод bar.


country_count = pd.value_counts(df['country'].values, sort=True)
country_count_keys, country_count_values = dict_sort(dict(country_count))    
TOP_COUNTRY = len(country_count_keys)
plt.title('Страны, где больше всего наблюдений', fontsize=PLOT_LABEL_FONT_SIZE)
plt.bar(np.arange(TOP_COUNTRY), country_count_values, color=getColors(TOP_COUNTRY))
plt.xticks(np.arange(TOP_COUNTRY), country_count_keys, rotation=0, fontsize=12)
plt.yticks(fontsize=PLOT_LABEL_FONT_SIZE)
plt.ylabel('Количество наблюдений', fontsize=PLOT_LABEL_FONT_SIZE)
plt.show()


g0tnandj9sjf3kl9-6s-giv2mbs.png

Больше всего наблюдений естественно в США. Тут ведь оно как, все гики, которые следят за НЛО живут в США (о версии, что таблица составлялась гражданами США, лукаво умолчим). Судя по количеству американских фильмов скорее всего второе. От Кэпа: если инопланетяне действительно посещали землю в открытую, то вряд ли бы их заинтересовала одна страна, сообщение об НЛО появлялись бы из разных стран.


Интересно еще посмотреть в какое время года наблюдали больше всего объектов. Есть резонное предположение, что больше всего наблюдений в весеннее время.


MONTH_COUNT = [0,0,0,0,0,0,0,0,0,0,0,0]
MONTH_LABEL = ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь',
    'Июль', 'Август', 'Сентябрь' ,'Октябрь' ,'Ноябрь' ,'Декабрь']

for i in df['datetime']:
    m,d,y_t =  i.split('/')
    MONTH_COUNT[int(m)-1] = MONTH_COUNT[int(m)-1] + 1

plt.bar(np.arange(12), MONTH_COUNT, color=getColors(12))
plt.xticks(np.arange(12), MONTH_LABEL, rotation=90, fontsize=PLOT_LABEL_FONT_SIZE)
plt.ylabel('Частота появления', fontsize=PLOT_LABEL_FONT_SIZE)
plt.yticks(fontsize=PLOT_LABEL_FONT_SIZE)
plt.title('Частота появления объектов по месяцам', fontsize=PLOT_LABEL_FONT_SIZE)
plt.show()


zqcsq0dwuipp7vphsse7gmqvz1a.png

Ожидалось весеннее обострение, но предположение не подтвердилось. Кажется теплые летние ночи и период отпусков дают о себе знать сильнее.


Посмотрим какие формы объектов на небе видели и сколько раз.


shapes_type_count = pd.value_counts(df['shape'].values)
shapes_type_count_keys, shapes_count_values = dict_sort(dict(shapes_type_count))
OBJECT_COUNT = len(shapes_type_count_keys)
plt.title('Типы объектов', fontsize=PLOT_LABEL_FONT_SIZE)
bar = plt.bar(np.arange(OBJECT_COUNT), shapes_type_count_values, color=getColors(OBJECT_COUNT))
plt.xticks(np.arange(OBJECT_COUNT), shapes_type_count_keys, rotation=90, fontsize=PLOT_LABEL_FONT_SIZE)
plt.yticks(fontsize=PLOT_LABEL_FONT_SIZE)
plt.ylabel('Сколько раз видели', fontsize=PLOT_LABEL_FONT_SIZE)
plt.show()


d4xcysefm7js4hnapimwv6vpxk8.png

Из графика мы видим, что больше всего на небе видели просто свет, который в принципе необязательно является НЛО. Этому явлению существует внятное объяснение, например, ночное небо отражает свет от прожекторов, как в фильмах про Бэтмена. Также это вполне может быть северным сиянием, которое появляется не только в полярной зоне, но и в средних широтах, а изредка даже и в близи эвкватора. Вообще атмосфера земли пронизана множеством излучений различной природы, электрическими и магнитными полями.


Вообще, атмосфера Земли пронизана множеством излучений различной природы, электрическими и магнитными полями.

Подробнее см.: https://www.nkj.ru/archive/articles/19196/ (Наука и жизнь, Что светится на небе?)

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


shapes_durations_dict = {}
for i in shapes_type_count_keys:
    dfs = df[['duration (seconds)', 'shape']].loc[df['shape'] == i]
    shapes_durations_dict[i] = dfs['duration (seconds)'].mean(axis=0)/60.0/60.0

shapes_durations_dict_keys = []
shapes_durations_dict_values = []

for k in shapes_type_count_keys:
    shapes_durations_dict_keys.append(k)
    shapes_durations_dict_values.append(shapes_durations_dict[k])
plt.title('Среднее время появление каждого объекта', fontsize=12)
plt.bar(np.arange(OBJECT_COUNT), shapes_durations_dict_values, color=getColors(OBJECT_COUNT))
plt.xticks(np.arange(OBJECT_COUNT), shapes_durations_dict_keys, rotation=90, fontsize=16)
plt.ylabel('Среднее время появления в часах', fontsize=12)
plt.show()


iyukfyi7xbcvz21s05ug4gba6y4.png

Из диаграммы видими, что больше всего в небе в среднем висел конус (более 20 часов). Если покопаться в интернетах, то ясно, что конусы в небе, это тоже свечение, только в виде конуса (неожиданно, да?). Вероятнее всего это свет от падающих комет. Среднее время больше 20 часов — это какая-то нереальная величина. В исследуемых данных большой разброс, и вполне могла вкраться ошибка. Несколько очень больших, неверных значений времени появления могут существенно исказить расчет среднего значения. Поэтому при больших отклонениях, считают не среднее значение, а медиану.


Медиана  — это некоторое число, характеризующее выборку, одна половина в выборке меньше этого числа, другая больше. Для расчета медианы используем функцию median.


Заменим в коде выше:


shapes_durations_dict[i] = dfs['duration (seconds)'].mean(axis=0)/60.0/60.0


на:


shapes_durations_dict[i] = dfs['duration (seconds)'].median(axis=0)/60.0/60.0


b50c171q-zkfqwqeluyozulgxtm.png

Полумесяц видели в небе чуть больше 5-ти часов. Другие объекты не надолго промелькнули в небе. Это уже наиболее достоверно.


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



Полезные ссылки:


Библиотека для предварительной обработки данных


Библиотека алгоритмов машинного обучения


Библиотека для визуализации данных


Туториал на русском языке по работе с Jupyter

© Habrahabr.ru