Анализ данных с использованием Python


Язык программирования 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

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


# Размер надписей на графиках
PLOT_LABEL_FONT_SIZE = 14 
# Генерация цветовой схемы
# Возвращает список цветов
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()


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


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


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()


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


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


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()


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


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

Подробнее см.: 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()


Из диаграммы видими, что больше всего в небе в среднем висел конус (более 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


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


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




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


Ссылка на архив с данными и блоткнотом Jupyter


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


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


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


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

Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 23

    +2
    Такие инструменты всё чаще применяются в исследованиях типа: «влияние мочи на космические лучи». Много данных и много красивых картинок, а вот толку мало.
      0
      Это же не проблема инструментов?
      +4
      конусы в небе, это тоже свечение, только в виде конуса

      Много конусов в небе
      image







      На всех фотографиях — МБР «Тополь», но Минитмэны, Союзы, Протоны, Арианы и т.п. выглядят точно так же.

        0

        Над США видят явно не "Тополя")))

          +5
          — А вы какую компанию представляете?
          — Акционерное общество «Московский Институт Теплотехники».
          — Хм. Во Франкфурте не знакомы с вашей продукцией.
          — Ну и не приведи вас господь.
          (Разговор на выставке)

          Если серьёзно, вот минитмэн III с конусом:

          image
        +1
        Наша цель будет не получить исчерпывающие ответы на главный вопрос жизни, вселенной и всего такого
        Так 42 же?

        Собственно, на месте НЛО могла быть любая таблица.
        «НЛО прилетело и опубликовало эту таблицу здесь.»
          0
          А где тут sklearn используется?
            0
            Верно, sklearn я в данной статье не использую. Просто изначально хотел классифицировать НЛО, по признакам с помощью sklearn, но это тема для уже статьи.
            0
            Про атмосферу дважды написано, исправь что б красиво было)
            Вообще, атмосфера Земли пронизана множеством излучений различной природы, электрическими и магнитными полями.
              +1
              Отличная статья.
              Надеюсь на продолжение.
              И хотелось бы чуть подробнее описание кода, в некоторых местах не хватает.
                0

                Для обработки дат, думаю, правильнее было перевести в datatime через функции pandas. Времени можно было бы сэкономить, да и сил тоже.

                  0
                  Ошибка в коде после «Посмотрим какие формы объектов на небе видели и сколько раз.»
                  Есть:
                  shapes_type_count_keys, shapes_count_values = dict_sort(dict(shapes_type_count))
                  Должно быть:
                  shapes_type_count_keys, shapes_type_count_values = dict_sort(dict(shapes_type_count))
                    0
                    Нормуль туториал для начинающих. Только сделайте его в виде Jupyter Notebook и вообще хорошо будет.
                      0
                      Я в Jupyter Notebook и писал. Могу ссылку дать по которой можно скачать блоткнот
                        0
                        Да прям в статью добавьте
                          +1
                          Ссылку на архив с блоткнотом добавил в конец статьи
                      0
                      Для создания сортированных словарей легче использовать стандартный модуль: docs.python.org/3/library/collections.html

                      Если тебе нужен «почти словарь», или «почти список»… — унаследуй его и перегрузи определенные функции, вместо написания внешней функции-обработчика. Это спровоцирует тебя осознанно подойти к выбору того, что и в какой момент делает твой тип. Ты соберешь информацию о поведении своего типа в одном месте. Ты сможешь сохранить правильное функционирование стандартных операторов сравнения, арифметики и т.д. Ты не сможешь забыть обернуть свой тип в свой обработчик в коде программы.

                      Использование for и pandas — плохой стиль, в общем случае. Pandas имеет встроенные оптимизированные механизмы обработки данных, которые будут работать быстрее for.
                      For часто используют с np для блобов, например работа с фото.
                      Или:
                      for df in [df1, df2, df3]:

                      В прочих случаях: df+for->освежи память
                        +1
                        Например для перевода я бы использовал «кэширующий переводчик» из dict.
                        В __init__ запихнул функцию инициализации Яндекса, а для __missing__(key) использовал функцию перевода.
                        Для перевода df использовал бы функцию apply, которой скормил бы «кэширующий переводчик».

                        ИМХО так красивее.
                          0
                          Самому сформировать сортированные словари, мне показалось проще и нагляднее. Насчет for в pandas — это дельный совет. Теперь знаю что лучше стараться не использовать for для перебора элементов в DataFrame.
                          +1

                          Под Windows 10 x64 (да я тот еще извращенец) не удалось поставить yandex-translate. Дефис надо поменять на точку: pip3 install yandex.translate

                            +1
                            Под Windows ставить не пробовал. Скорее это pip такой под винду
                            0
                            В статье используется константа PLOT_LABEL_FONT_SIZE, но ни где не сказано из какого модуля она вытянута или где она объявляется.
                            Не подскажите что за константа?
                              0
                              Константа объявлена в начале программы. Когда заливал статью забыл скопировать, сейчас исправил.
                              # Размер надписей на графиках
                              PLOT_LABEL_FONT_SIZE = 14 
                              # Генерация цветовой схемы
                              # Возвращает список цветов
                              def getColors(n):
                                  COLORS = []
                                  cm = plt.cm.get_cmap('hsv', n)
                                  for i in np.arange(n):
                                      COLORS.append(cm(i))
                                  return COLORS

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое