однострочники пайтон. Однострочники Python лаконичный и содержательный код by Кристи. Однострочники
Скачать 4.44 Mb.
|
Принцип работы Мы создали список с помощью спискового включения (подробности о том, что представляют собой выражение + контекст, см. в разделе «Поиск самых Итоги главы 69 высокооплачиваемых работников с помощью спискового включения» на с. 42). Контекст состоит из кортежей для всех строк в переменной db_rows . Выраже- ние zip(column_names, row) упаковывает вместе схему и строки. Например, первым из созданных списковым включением элементов будет zip(['name', 'salary', 'job'], ('Alice', 180000, 'data scientist')) , объект, который по- сле преобразования в список приобретает вид [('name', 'Alice'), ('salary', 180000), ('job', 'data scientist')] . Форма элементов — (ключ, значение) , поэтому можно преобразовать их в ассоциативный массив с помощью функ- ции преобразования dict() , чтобы получить желаемый формат базы данных. ПРИМЕЧАНИЕ Для функции zip() не важно, что одно из входных значений — список, а другое — кортеж. Ей необходимо только, чтобы входные данные пред- ставляли собой итерируемые объекты (а и списки, и кортежи — итери- руемые). Результаты выполнения этого однострочного фрагмента кода таковы: ## Результат print(db) ''' [{'name': 'Alice', 'salary': 180000, 'job': 'data scientist'}, {'name': 'Bob', 'salary': 99000, 'job': 'mid-level manager'}, {'name': 'Frank', 'salary': 87000, 'job': 'CEO'}] ''' Теперь всем элементам данных соответствуют названия в списке ассоциа- тивных массивов. Вы научились эффективно использовать функцию zip() Итоги главы В этой главе вы научились работать со списковыми включениями, вводом данных из файлов, лямбда-функциями, функциями map() и zip() , кванти- фикатором all() , срезами и выполнять простейшие операции со списками. Вы также научились использовать структуры данных для решения разно- образных повседневных задач. Легкое преобразование структур данных туда и обратно — навык, суще- ственно влияющий на скорость написания кода. Можете не сомневать- ся — ваши темпы написания кода резко возрастут, как только вы научитесь 70 Глава 2. Трюки Python эффективнее выполнять различные операции с данными. Небольшие задачи по обработке данных наподобие тех, что встречались в главе, вносят немалый вклад в распространенную «смерть от тысячи порезов» — это огромный вред для общей производительности программиста. Благодаря приведенным в главе приемам, функциям и возможностям Python вы сможете эффективно защититься от этой «тысячи порезов». Образно говоря, эти новоприобре- тенные инструменты помогут вам гораздо быстрее оправиться от каждого из «порезов». В следующей главе вы еще более усовершенствуете свои навыки работы с data science благодаря знакомству с новым набором инструментов, предо- ставляемых библиотекой NumPy для численных вычислений на языке Python. РЕШЕНИЕ УПРАЖНЕНИЯ 2.1 Вот как можно решить вышеупомянутую задачу фильтрации всех строк, со- держащих строку символов 'anonymous', с помощью спискового включения вместо функции map(). В данном случае я даже рекомендую использовать именно списковое включение — это более быстрый и аккуратный способ. mark = [(True, s) if 'anonymous' in s else (False, s) for s in txt] 3 Наука о данных Умение анализировать реальные данные — один из наиболее востребованных навыков в XXI столетии. Благодаря мощному аппаратному обеспечению, ал- горитмам и вездесущим датчикам, исследователи данных извлекают смысл из больших массивов необ- работанных данных о погоде, финансовых транзакциях, поведении покупателей и многом другом. Крупнейшие на сегодняшний день компании в мире — Google, Facebook, Apple и Amazon — по существу представляют собой гигантские заводы по обработке данных, и данные лежат в самой сердцевине их бизнес-моделей. В этой главе вы научитесь обрабатывать и анализировать числовые дан- ные с помощью библиотеки для численных вычислений языка Python NumPy. Я приведу десять реальных задач и объясню, как решить их одной строкой кода NumPy. А поскольку NumPy лежит в основе многих высокоуровневых библиотек для исследования данных и машинного обу- чения (Pandas, scikit-learn и TensorFlow, например), тщательное изучение данной главы повысит вашу рыночную стоимость в условиях нынешней, ориентированной на работу с данными экономики. Так что уделите мне все свое внимание! 72 Глава 3. Наука о данных Простейшие операции с двумерными массивами В этом разделе с помощью одной строки кода вам предстоит решить по- вседневную бухгалтерскую задачу. Я познакомлю вас с функциональностью NumPy, важнейшей библиотеки языка Python для численных вычислений и исследования данных. Общее описание Важнейший элемент библиотеки NumPy — массивы NumPy, используемые для хранения данных, предназначенных для анализа, визуализации и раз- личных операций. Многие более высокоуровневые библиотеки data science, например Pandas, напрямую или опосредованно основаны на массивах NumPy. Массивы NumPy аналогичны спискам Python, но имеют некоторые допол- нительные преимущества. Во-первых, массивы NumPy занимают меньше места в памяти и чаще всего отличаются большим быстродействием. Во- вторых, массивы NumPy удобнее при обращении более чем к двум осям координат, то есть для многомерных данных (доступ к многомерным спискам и их модификация — непростые задачи). А поскольку массивы NumPy мо- гут содержать несколько осей координат, мы будем рассматривать массивы через призму измерений (dimensions): включающий две оси координат мас- сив — двумерный. В-третьих, функциональность доступа к массивам NumPy намного шире и включает транслирование, с которым вы познакомитесь ближе в этой главе. В листинге 3.1 приведены примеры создания одномерного, двумерного и трехмерного массивов NumPy. Листинг 3.1. Создание одномерного, двумерного и трехмерного массивов в NumPy import numpy as np # Создание одномерного массива из списка a = np.array([1, 2, 3]) print(a) """ [1 2 3] """ # Создание двумерного массива из списка списков Простейшие операции с двумерными массивами 73 b = np.array([[1, 2], [3, 4]]) print(b) """ [[1 2] [3 4]] """ # Создание трехмерного массива из списка списков списков c = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]]) print(c) """ [[[1 2] [3 4]] [[5 6] [7 8]]] """ Сначала мы импортировали библиотеку NumPy в наше пространство имен, указав практически стандартное название для нее: np . После импорта библио- теки мы создали массив NumPy, передав обычный список Python в качестве аргумента в функцию np.array() . Одномерный массив соответствует про- стому списку числовых значений (на самом деле массивы NumPy могут содержать и данные других типов, но здесь мы сосредоточим свое внимание на числах). Двумерный массив соответствует вложенному списку списков числовых значений, а трехмерный — вложенному списку списков списков числовых значений. Размерность массива NumPy определяется из количе- ства открывающих и закрывающих скобок. Массивы NumPy обладают более широкими возможностями, чем встроен- ные списки Python. Например, над двумя массивами NumPy можно выпол- нять простейшие арифметические операции + , – , * и / . Эти поэлементные операции над массивами (например, сложение их с помощью оператора + ) заключаются в выполнении соответствующей операции над каждым из элементов массива a с соответствующим элементом массива b . Другими словами, поэлементная операция агрегирует два элемента, расположенных на одинаковых местах в массивах a и b . В листинге 3.2 приведены примеры простейших арифметических операций над двумерными массивами. Листинг 3.2. Простейшие арифметические операции с массивами import numpy as np a = np.array([[1, 0, 0], 74 Глава 3. Наука о данных [1, 1, 1], [2, 0, 0]]) b = np.array([[1, 1, 1], [1, 1, 2], [1, 1, 2]]) print(a + b) """ [[2 1 1] [2 2 3] [3 1 2]] """ print(a - b) """ [[ 0 -1 -1] [ 0 0 -1] [ 1 -1 -2]] """ print(a * b) """ [[1 0 0] [1 1 2] [2 0 0]] """ print(a / b) """ [[1. 0. 0. ] [1. 1. 0.5] [2. 0. 0. ]] """ ПРИМЕЧАНИЕ При использовании операторов NumPy к массивам целых чисел библио- тека пытается сгенерировать в качестве результата также массив целых чисел. Только при делении двух массивов целых чисел с помощью опера- тора деления, a / b, результат будет массивом чисел с плавающей точкой, на что указывают десятичные точки: 1., 0. и 0.5. Если присмотреться, вы заметите, что все эти операции группируют два соответствующих массива NumPy поэлементно. При сложении двух мас- сивов результат представляет собой новый массив: каждое новое значение Простейшие операции с двумерными массивами 75 представляет собой сумму двух соответствующих значений из первого и вто- рого массивов. То же самое относится и к вычитанию, умножению и делению. NumPy предоставляет и многие другие средства для работы с массивами, включая функцию np.max() , вычисляющую максимальное из всех значений массива NumPy. Функция np.min() вычисляет минимальное из всех значе- ний массива NumPy. Функция np.average() вычисляет среднее значение массива NumPy. В листинге 3.3 приведены примеры этих трех операций. Листинг 3.3. Вычисление максимального, минимального и среднего значений массива NumPy import numpy as np a = np.array([[1, 0, 0], [1, 1, 1], [2, 0, 0]]) print(np.max(a)) # 2 print(np.min(a)) # 0 print(np.average(a)) # 0.6666666666666666 Максимальное из всех значений данного массива NumPy равно 2 , мини- мальное — 0 , а среднее: (1 + 0 + 0 + 1 + 1 + 1 + 2 + 0 + 0) / 9 = 2/3 . NumPy включает множество мощных инструментов, но и этих вполне достаточно для решения следующей задачи: как найти максимальный доход после уплаты налогов среди группы людей, если известны годовая зарплата и ставка налогообложения. Код Попробуем решить эту задачу на основе данных о зарплате Алисы, Боба и Тима. Похоже, что у Боба последние три года самая высокая зарплата из наших троих друзей. Но приносит ли он домой действительно больше всех денег с учетом индивидуальных ставок налогообложения? Посмотрим на листинг 3.4. 76 Глава 3. Наука о данных Листинг 3.4. Однострочное решение на основе простейших арифметических операций с массивами ## Зависимости import numpy as np ## Данные: годовые зарплаты в тысячах долларов (за 2017, 2018 и 2019 гг.) alice = [99, 101, 103] bob = [110, 108, 105] tim = [90, 88, 85] salaries = np.array([alice, bob, tim]) taxation = np.array([[0.2, 0.25, 0.22], [0.4, 0.5, 0.5], [0.1, 0.2, 0.1]]) ## Однострочник max_income = np.max(salaries - salaries * taxation) ## Результат print(max_income) Попробуйте догадаться: каковы будут результаты выполнения этого кода? Принцип работы После импорта библиотеки NumPy мы помещаем данные в двумерный массив NumPy, содержащий три строки (по одной строке для каждого человека: Алисы, Боба и Тима) и три столбца (по одному столбцу для каждого года: 2017, 2018 и 2019). У нас два двумерных массива: salaries содержит годовой доход, а taxation — ставки налогообложения по всем людям и годам. Для вычисления чистого дохода необходимо вычесть налоги (в долларах) из валового дохода, хранящегося в массиве salaries . Для этого мы вос- пользуемся перегруженными операторами NumPy – и * , выполняющими поэлементные вычисления с массивами NumPy. Поэлементное произведение двух многомерных массивов называется про- изведением Адамара. Листинг 3.5 демонстрирует, как выглядит массив NumPy после вычитания налогов из валового дохода. Как видим из второй строки, высокий доход Боба существенно снизился после уплаты 40 и 50 % налогов. Работа с массивами NumPy: срезы, транслирование и типы массивов 77 Листинг 3.5. Простейшие арифметические операции над массивами print(salaries - salaries * taxation) """ [[79.2 75.75 80.34] [66. 54. 52.5 ] [81. 70.4 76.5 ]] """ Предыдущий фрагмент кода выводит максимальное значение этого итого- вого массива. Функция np.max() просто находит в массиве максимальное значение, которое мы затем сохраняем в переменной max_income . Максималь- ным значением оказывается доход Тима, равный 90 000 долларов в 2017 году, облагаемый налогом всего 10 % — результат выполнения однострочника равен 81. (опять же, точка в конце указывает на тип данных float). С помощью простейших поэлементных арифметических операций над массивами NumPy мы проанализировали ставки налогов группы людей. Воспользуемся тем же набором данных для демонстрации более сложных понятий NumPy: срезов и транслирования. Работа с массивами NumPy: срезы, транслирование и типы массивов Данный однострочник демонстрирует мощь трех интересных возможностей NumPy: срезов, транслирования и типов массивов. Наши данные представ- ляют собой массив, содержащий различные профессии и соответствующие зарплаты. Мы воспользуемся всеми этими тремя понятиями, чтобы каждые два года повышать зарплаты одних только исследователей данных на 10 %. Общее описание Основная загвоздка в нашей задаче — как поменять конкретные значения в многострочном массиве NumPy. Нам нужно поменять каждое второе значение для одной конкретной строки. Посмотрим на основные понятия, которые необходимы для решения этой задачи. Срезы и доступ по индексу Доступ по индексу и срезы в NumPy аналогичны доступу по индексу и срезам в Python (см. главу 2): к элементам одномерного массива можно обращаться 78 Глава 3. Наука о данных путем указания в квадратных скобках [] индекса или диапазона индексов. Например, операция доступа по индексу x[3] возвращает четвертый элемент массива NumPy x (поскольку индекс первого элемента — 0). Можно также использовать доступ по индексу для многомерных масси- вов путем указания индекса для каждого измерения по отдельности либо разделенных запятыми индексов для доступа к различным измерениям. Например, операция доступа по индексу y[0,1,2] служит для обращения к первому элементу первой оси координат, второму элементу второй оси координат и третьему элементу третьей оси координат. Учтите, что такой синтаксис не подходит для многомерных списков Python. Перейдем теперь к срезам в NumPy. Изучите примеры в листинге 3.6, чтобы разобраться с одномерными срезами в NumPy, и не стесняйтесь снова обратиться к главе 2, если у вас возникнут какие-либо проблемы с их пониманием. Листинг 3.6. Примеры одномерных срезов import numpy as np a = np.array([55, 56, 57, 58, 59, 60, 61]) print(a) # [55 56 57 58 59 60 61] print(a[:]) # [55 56 57 58 59 60 61] print(a[2:]) # [57 58 59 60 61] print(a[1:4]) # [56 57 58] print(a[2:-2]) # [57 58 59] print(a[::2]) # [55 57 59 61] print(a[1::2]) # [56 58 60] print(a[::-1]) # [61 60 59 58 57 56 55] print(a[:1:-2]) # [61 59 57] Работа с массивами NumPy: срезы, транслирование и типы массивов 79 print(a[-1:1:-2]) # [61 59 57] Следующий этап — полностью разобраться с многомерными срезами. Доступ по индексу выполняется путем применения одномерных срезов по отдель- ности для каждой оси координат (разделенных запятыми), чтобы выбрать диапазоны элементов по этой оси. Не пожалейте времени, чтобы тщательно разобраться с примерами в листинге 3.7. Листинг 3.7. Примеры многомерных срезов import numpy as np a = np.array([[0, 1, 2, 3], [4, 5, 6, 7], [8, 9, 10, 11], [12, 13, 14, 15]]) print(a[:, 2]) # Третий столбец: [ 2 6 10 14] print(a[1, :]) # Вторая строка: [4 5 6 7] print(a[1, ::2]) # Вторая строка, каждый второй элемент: [4 6] print(a[:, :-1]) # Все столбцы, за исключением последнего: # [[ 0 1 2] # [ 4 5 6] # [ 8 9 10] # [12 13 14]] print(a[:-2]) # Аналогично a[:-2, :] # [[ 0 1 2 3] # [ 4 5 6 7]] Изучайте листинг 3.7 до тех пор, пока не будете уверены, что хорошо пони- маете принципы многомерных срезов. Двумерный срез можно производить с помощью синтаксиса a[срез1, срез2] . Для дополнительных измерений необходимо добавить через запятую дополнительные операции срезов (с по- мощью операторов срезов начало:конец или начало:конец:шаг ). Каждый срез служит для выбора отдельной подпоследовательности элементов соответ- ствующего измерения. Понимания этой основной идеи вполне достаточно для перехода от одномерных к многомерным срезам. 80 Глава 3. Наука о данных Транслирование Транслирование (broadcasting) означает автоматический процесс приведе- ния двух массивов NumPy к одной форме для применения определенных поэлементных операций (см. подраздел «Срезы и доступ по индексу» на с. 77). Транслирование тесно связано с атрибутом формы массивов NumPy, который, в свою очередь, тесно связан с понятием осей координат. Так что займемся изучением осей координат, форм и транслирования. Каждый массив охватывает несколько осей координат, по одной для каждого измерения (листинг 3.8). Листинг 3.8. Оси координат и размерность трех массивов NumPy import numpy as np a = np.array([1, 2, 3, 4]) print(a.ndim) # 1 b = np.array([[2, 1, 2], [3, 2, 3], [4, 3, 4]]) print(b.ndim) # 2 c = np.array([[[1, 2, 3], [2, 3, 4], [3, 4, 5]], [[1, 2, 4], [2, 3, 5], [3, 4, 6]]]) print(c.ndim) # 3 Здесь мы видим три массива: a , b и c . В атрибуте ndim массива хранит- ся количество его осей координат. Мы просто вывели его в командную оболочку для каждого из массивов. Массив a — одномерный, массив b — двумерный, а массив c — трехмерный. У каждого из этих массивов есть атрибут, указывающий его форму: кортеж, содержащий количество элементов по каждой оси. У двумерного массива — два значения в этом кортеже: количество строк и количество столбцов. Для массивов большей размерности i-е значение кортежа задает количество элементов по i-й оси. Следовательно, количество элементов этого кортежа соответствует раз- мерности массива NumPy. |