На этом шаге мы будем изучать данные о загрязненности воздуха в городах. А именно, по представленному двумерному массиву
NumPy с данными измерений загрязнений (столбцы) для нескольких городов (строки) мы найдем города с загрязнением выше среднего.
Полученные при чтении этого раздела навыки пригодятся вам при поиске аномальных значений в различных наборах данных.
Индекс качества воздуха (Air Quality Index, AQI) служит для оценки опасности вредного воздействия на здоровье и часто применяется для сравнения качества воздуха в различных городах. В следующем однострочнике мы будем исследовать AQI четырех городов: Гонконга, Нью-Йорка, Берлина и Монреаля.
Данный однострочник выявляет города, загрязненные выше среднего, то есть такие, максимальное значение AQI которых выше общего среднего значения по всем измерениям всех городов.
Важная составляющая нашего решения: поиск элементов в массиве NumPy, удовлетворяющих заданному условию. Это распространенная задача в data science, с которой вы будете сталкиваться очень часто.
Итак, разберемся, как найти элементы массива, удовлетворяющие определенному условию. NumPy предоставляет функцию nonzero() для поиска индексов элементов в массиве, которые не равны нулю. Посмотрите на пример 3.12.
import numpy as np X = np.array([[1, 0, 0], [0, 2, 2], [3, 0, 0]]) print(np.nonzero(X))
Результат представляет собой кортеж из двух массивов NumPy:
(array([0, 1, 1, 2], dtype=int64), array([0, 1, 2, 0], dtype=int64))
Первый массив содержит индексы строк, а второй - индексы столбцов ненулевых элементов. В исходном двумерном массиве содержится четыре ненулевых элемента: 1, 2, 2 и 3, на позициях X[0,0], X[1,1], X[1,2] и X[2,0].
Как же с помощью функции nonzero() найти в массиве элементы, удовлетворяющие определенному условию? Для этого обратимся еще к одной замечательной возможности NumPy: булевым операциям над массивами, выполняемым с помощью транслирования (пример 3.13)!
import numpy as np X = np.array([[1, 0, 0], [0, 2, 2], [3, 0, 0]]) print(X == 2) # [[False False False] # [False True True] # [False False False]]
Транслирование происходит при копировании (по существу) целочисленного значения 2 в новый массив той же формы, что и исходный. Далее NumPy производит поэлементное сравнение всех целочисленных значений со значением 2 и возвращает полученный в результате булев массив.
В нашем основном коде для поиска элементов, удовлетворяющих определенному условию, мы воспользуемся сочетанием функции nonzero() и операций
над булевыми массивами.
В примере 3.14 мы найдем в наборе данных города с максимумами загрязнения, превышающими среднее значение.
## Зависимости import numpy as np ## Данные: измерения индекса качества воздуха, AQI (строка = город) X = np.array( [[42, 40, 41, 43, 44, 43], # Гонконг [30, 31, 29, 29, 29, 30], # Нью-Йорк [8, 13, 31, 11, 11, 9], # Берлин [11, 11, 12, 13, 11, 12]]) # Монреаль cities = np.array(["Hong Kong", "New York", "Berlin", "Montreal"]) ## Однострочник polluted = set(cities[np.nonzero(X > np.average(X))[0]]) ## Результат print(polluted)
Можете определить, какими будут результаты выполнения этого кода?
Массив данных X содержит четыре строки (по одной для каждого города) и шесть столбцов (по одному для каждого отрезка измерения - в данном случае дня). Строковый массив cities содержит четыре названия городов в том порядке, в каком те встречаются в массиве с данными.
Вот однострочник для поиска городов, в которых наблюдается уровень AQI выше среднего:
## Однострочник polluted = set(cities[np.nonzero(X > np.average(X))[0]])
Чтобы понять, как он работает в целом, необходимо сначала разобраться в каждой из его составных частей. Проанализируем его, начав изнутри. В его сердцевине находится операция над булевым массивом (пример 3.15).
print(X > np.average(X)) # [[True True True True True True] # [True True True True True True] # [False False True False False False] #[False False False False False False]]
Чтобы привести оба операнда к одной форме с помощью транслирования, мы воспользовались булевым выражением. Для вычисления среднего по всем элементам нашего массива NumPy значения AQI мы задействуем функцию np.average(). Далее булево выражение производит поэлементное сравнение, и получается булев массив, содержащий True, если соответствующее измерение превышает среднее значение AQI.
Благодаря генерации этого булева массива мы знаем в точности, какие элементы удовлетворяют условию "выше среднего", а какие - нет.
Напомним, что значение True языка Python представлено значением 1 типа integer, а False - 0. На самом деле тип объектов True и False - bool - является подклассом int. Таким образом, каждое булево значение является также и целочисленным значением. Благодаря этому мы можем воспользоваться функцией nonzero() для поиска всех удовлетворяющих условию индексов строк и столбцов, вот так:
print(np.nonzero(X > np.average(X))) # (array([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2], dtype=int64), array([0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 2], dtype=int64))
Получаем два кортежа, первый - с индексами строк ненулевых элементов, а второй - с индексами соответствующих им столбцов.
Нам нужны только названия городов со значениями AQI выше среднего, и ничего больше, поэтому нас интересуют только индексы строк, которыми мы можем воспользоваться для извлечения символьных названий городов из нашего строкового массива с помощью расширенного доступа по индексу (advanced indexing) - методики доступа по индексу, позволяющей описывать не непрерывную последовательность индексов массива.
Таким образом можно обращаться к произвольным элементам данного массива NumPy, указывая последовательность либо целых чисел (выбираемых индексов), либо булевых значений (для выбора тех индексов, для которых соответствующее булево значение равно True):
print(cities[np.nonzero(X > np.average(X))[0]]) # ['Hong Kong', 'Hong Kong', 'Hong Kong', 'Hong Kong', 'Hong Kong', 'Hong Kong', # 'New York', 'New York', 'New York', 'New York', 'New York', 'New York', 'Berlin']
Как видите, в полученной последовательности строковых значений немало повторов, поскольку в числе измерений AQI Гонконга и Нью-Йорка много значений выше среднего.
Осталось только убрать эти повторы. Для этого мы преобразуем нашу последовательность во множество Python, в котором по определению отсутствуют дублирующиеся значения, и получим краткую сводку названий всех городов, степень загрязнения воздуха в которых превышает средние значения AQI.
Резюмируя: вы научились использовать булевы выражения для массивов NumPy (опять же, с помощью транслирования) и функцию nonzero() для поиска строк или столбцов, удовлетворяющих определенным условиям. Позанимавшись охраной окружающей среды в этом однострочнике, перейдем к влиятельным блогерам в социальных медиа.
На следующем шаге мы рассмотрим фильтрацию двумерных массивов с помощью булева доступа по индексу.