Часто используемый синтаксис Python в науке о данных (основы)

В эти дни я читаю книгу Data Science from Scratch (PDF-версия), которая является отличным и доступным введением в науку о данных. Одна из глав посвящена базовому синтаксису Python и продвинутым функциям, часто используемым в науке о данных. Мне понравилось, как четко и лаконично это было изложено, поэтому я решил перевести этот материал для себя и поделиться им здесь в качестве шпаргалки.
Часто используемый синтаксис Python в науке о данных (основы)
Часто используемый синтаксис Python в науке о данных (продвинутый уровень)

Эта глава посвящена базовому синтаксису и функционалу Python, чрезвычайно полезным при обработке данных (на основе Python 2.7).

Отступы

В то время как многие языки используют скобки для управления блоками кода, Python полагается на отступы:

for i in [1, 2, 3, 4, 5]:  
    print i          # Первая строка цикла "for i"  
    for j in [1, 2, 3, 4, 5]:  
        print j      # Первая строка цикла "for j"  
        print i + j  # Последняя строка цикла "for j"  
    print i          # Последняя строка цикла "for i"  
print "done looping"  

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

long_winded_computation = (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20)  

Это также улучшает читаемость кода:

list_of_lists = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]  
easier_to_read_list_of_lists = [ [1, 2, 3],  
                                 [4 ,5 ,6 ],  
                                 [7 ,8 ,9 ] ]  

Многострочные выражения

Можно использовать обратную косую черту для обозначения продолжения строки (это редко применяется):

two_plus_three = 2 + \
                 3  

Модули

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

  1. Просто импортируйте весь модуль:
import re  
my_regex = re.compile("[0-9]+", re.I)  

Здесь импортируется модуль re, предназначенный для работы с регулярными выражениями. После импорта вы можете вызывать его функции, используя имя модуля в качестве префикса (например, re.compile).

  1. Если имя импортируемого модуля уже используется в вашем коде, вы можете импортировать его под другим именем:
import re as regex  
my_regex = regex.compile("[0-9]+", regex.I)  
  1. Если вы очень плохо себя ведете, вы можете импортировать весь модуль в текущее пространство имен, что может случайно перезаписать уже определенные вами переменные:
match = 10  
from re import *  # В модуле re есть функция match  
print match       # Выводит функцию match  

Но поскольку вы хороший человек, я уверен, что вы так не поступите.

Арифметические операции

По умолчанию Python 2.7 использует целочисленное деление, поэтому $5 / 2 = 2$. Однако часто нам не нужно целочисленное деление, поэтому можно импортировать этот модуль:

from __future__ import division  

После импорта получаем $5 / 2 = 2.5$.
Целочисленное деление: $5 // 2 = 2$.

Функции

Определение функции

Функция — это набор правил, который принимает ноль или более входных данных и возвращает определенный результат. В Python мы определяем функцию с помощью def имя_функции(параметры):

def double(x):  
    """Здесь вы можете написать объяснение функционала функции.  
    Например, эта функция умножает входное значение на 2."""  
    # Здесь можно написать тело функции, не забудьте про отступ  
    return x * 2  

Использование функции

В Python функции являются объектами первого класса, что означает, что мы можем присваивать их переменным или передавать их в качестве аргументов другим функциям:

def apply_to_one(f):  
    """Вызывает функцию f и передает 1 в качестве аргумента."""  
    return f(1)  
my_double = double          # double ссылается на функцию, определенную в предыдущем разделе  
x = apply_to_one(my_double) # x равен 2  

Анонимные функции

Также можно создавать анонимные функции с помощью lambda:

y = apply_to_one(lambda x: x + 4)     # Равно 5  

Можно присвоить lambda-функцию переменной, но большинство посоветует вам по возможности использовать def:

another_double = lambda x: 2 * x      # Не рекомендуется  
def another_double(x): return 2 * x   # Рекомендуемый способ  

Дополнительно:

Передача аргументов функции

Параметры функции могут иметь значения по умолчанию. Если вы не передаете аргумент, будет использовано значение по умолчанию; если передаете, будет использовано указанное значение:

def my_print(message="my default message"):  
    print message  
my_print("hello")     # Выводит "hello"  
my_print()            # Выводит "my default message"  

Иногда очень удобно указывать аргументы напрямую по их имени:

def subtract(a=0, b=0):  
    return a - b  
subtract(10, 5)   # Возвращает 5  
subtract(0, 5)    # Возвращает -5  
subtract(b=5)     # То же самое, что и предыдущее, возвращает -5  

Строки

Для создания строк можно использовать одинарные или двойные кавычки (кавычки должны быть парными):

single_quoted_string = 'data science'  
double_quoted_string = "data science"  

Обратная косая черта используется для обозначения escape-последовательностей, например:

tab_string = "\t"      # Обозначает символ табуляции  
len(tab_string)        # Равно 1  

Если вы хотите использовать саму обратную косую черту (например, для путей Windows или регулярных выражений), вы можете определить строку как “сырую” с помощью r"":

not_tab_string = r"\t" # Обозначает символы '\' и 't'  
len(not_tab_string)    # Равно 2  

Для создания многострочных строк используйте тройные двойные кавычки:

multi_line_string = """Это первая строка  
Это вторая строка  
Это третья строка"""  

Обработка исключений

Когда программа сталкивается с ошибкой, Python генерирует исключение. Если мы не обработаем его, выполнение программы прекратится. Исключения можно перехватывать с помощью операторов try и except:

try:  
    print 0 / 0  
except ZeroDivisionError:  
    print "Нельзя делить на ноль"  

Хотя в других языках исключения иногда рассматриваются как нежелательное явление, в Python их активная обработка может сделать ваш код более чистым и лаконичным.

Списки

Создание списков

Списки — это простые упорядоченные коллекции, одна из самых базовых структур данных в Python (похожи на массивы в других языках, но с некоторыми дополнительными особенностями). Чтобы создать список:

integer_list = [1, 2, 3]  
heterogeneous_list = ["string", 0.1, True]  
list_of_lists = [ integer_list, heterogeneous_list, [] ]  
list_length = len(integer_list)   # Равно 3  
list_sum = sum(integer_list)      # Равно 6  

Доступ к элементам списка

Вы можете получать доступ к элементам списка по индексу в квадратных скобках:

x = range(10)       # Получаем список x = [0, 1, ..., 9]  
zero = x[0]         # Равно 0, индексация списка начинается с 0  
one = x[1]          # Равно 1  
nine = x[-1]        # Равно 9, последний элемент списка  
eight = x[-2]       # Равно 8, предпоследний элемент списка  
x[0] = -1           # Теперь список x = [-1, 1, 2, 3, ..., 9]  

Срезы списков

Списки можно срезать с помощью квадратных скобок:

first_three = x[:3]                  # [-1, 1, 2]  
three_to_end = x[3:]                 # [3, 4, ..., 9]  
one_to_four = x[1:5]                 # [1, 2, 3, 4]  
last_three = x[-3:]                  # [7, 8, 9]  
without_first_and_last = x[1:-1]     # [1, 2, ..., 8]  
copy_of_x = x[:]                     # [-1, 1, 2, ..., 9]  

С помощью in можно проверить, содержится ли элемент в списке:

1 in [1, 2, 3]        # True  
0 in [1, 2, 3]        # False  

Этот способ поиска элементов неэффективен. Используйте его только для очень маленьких списков или если вас не волнует время поиска.

Объединение списков

В Python очень легко объединять два списка:

x = [1, 2, 3]  
x.extend([4, 5, 6])   # Теперь x = [1, 2, 3, 4, 5, 6]  

Если вы не хотите изменять исходный список x, вы можете использовать оператор сложения для создания нового списка:

x = [1, 2, 3]  
y = x + [4, 5, 6]     # Теперь y = [1, 2, 3, 4, 5, 6]; x не изменился  

Часто элементы добавляются в список по одному следующим образом:

x = [1, 2, 3]  
x.append(0)           # Теперь x = [1, 2, 3, 0]  
y = x[-1]             # Равно 0  
z = len(x)            # Равно 4  

Распаковка списка

Если вы знаете количество элементов в списке, его легко можно распаковать:

x, y = [1, 2]         # Теперь x = 1, y = 2  

Если количество элементов с обеих сторон присваивания не совпадает, вы получите ошибку значения. Поэтому чаще мы используем нижнее подчеркивание для оставшейся части списка:

_, y = [1, 2]         # Теперь y == 2, первый элемент игнорируется  

Кортежи

Кортежи очень похожи на списки, с единственной разницей: элементы кортежа нельзя изменять.

Создание кортежей

Кортежи можно создавать с помощью круглых скобок или вообще без них:

my_tuple = (1, 2)  
other_tuple = 3, 4  
my_list[1] = 3        # Теперь my_list = [1, 3]  
try:  
    my_tuple[1] = 3  
except TypeError:  
    print "Невозможно изменить кортеж"  

Кортежи очень удобны для получения нескольких возвращаемых значений из функции:

def sum_and_product(x, y):  
    return (x + y),(x * y)  
sp = sum_and_product(2, 3)    # Равно (5, 6)  
s, p = sum_and_product(5, 10) # s = 15, p = 50  

Кортежи (и списки) поддерживают одновременное присваивание нескольких элементов:

x, y = 1, 2       # Теперь x = 1, y = 2  
x, y = y, x       # Обмен значений двух переменных в Python; теперь x = 2, y = 1  

Словари

Создание словарей

Еще одна базовая структура данных в Python — это словари, которые позволяют быстро получать значения по ключу:

empty_dict = {}                       # Очень питоновское определение пустого словаря  
empty_dict2 = dict()                  # Менее питоновское определение пустого словаря  
grades = { "Joel" : 80, "Tim" : 95 }  # Хранение словаря  

Доступ к элементам словаря

Вы можете использовать квадратные скобки с ключом для поиска соответствующего значения:

joels_grade = grades["Joel"]          # Равно 80  

Если искомого ключа нет в словаре, будет возвращена ошибка ключа (KeyError):

try:  
    kates_grade = grades["Kate"]  
except KeyError:  
    print "Нет оценки для Кейт!"  

С помощью in можно проверить, содержится ли ключ в словаре:

joel_has_grade = "Joel" in grades     # True  
kate_has_grade = "Kate" in grades     # False  

У словарей есть метод, который возвращает значение по умолчанию, если искомого ключа нет в словаре (вместо генерации исключения):

joels_grade = grades.get("Joel", 0)   # Равно 80  
kates_grade = grades.get("Kate", 0)   # Равно 0  
no_ones_grade = grades.get("No One")  # Возвращает значение по умолчанию None  

Изменение словарей

Пары ключ-значение в словаре можно создавать и изменять с помощью квадратных скобок:

grades["Tim"] = 99                    # Заменяет старое значение  
grades["Kate"] = 100                  # Добавляет новую пару ключ-значение  
num_students = len(grades)            # Равно 3  

Мы часто будем использовать словари для представления структур данных следующим образом:

tweet = {  
    "user" : "joelgrus",  
    "text" : "Data Science is Awesome",  
    "retweet_count" : 100,  
    "hashtags" : ["#data", "#science", "#datascience", "#awesome", "#yolo"]  
}  

Помимо поиска конкретных ключей, мы также можем работать со всеми ключами следующим образом:

tweet_keys = tweet.keys()             # Получаем список ключей  
tweet_values = tweet.values()         # Получаем список значений  
tweet_items = tweet.items()           # Получаем список кортежей (ключ, значение)  
"user" in tweet_keys                  # Возвращает True, используется неэффективный поиск 'in' в списке  
"user" in tweet                       # Более питоновский способ, используется эффективный поиск 'in' в словаре  
"joelgrus" in tweet_values            # True  

Ключи в словарях уникальны, и списки не могут быть использованы в качестве ключей словаря. Если вам нужен составной ключ, вы можете использовать кортежи или преобразовать ключ в строку.

Словари по умолчанию (Default Dictionaries)

Если вы пытаетесь подсчитать частоту каждого слова в документе, очевидный подход — создать словарь, где слова будут ключами, а их частоты — значениями. Затем вы проходите по документу, увеличивая значение для уже встреченных слов и добавляя новую пару ключ-значение для еще не встречавшихся:

word_counts = {}  
for word in document:  
    if word in word_counts:  
        word_counts[word] += 1  
    else:  
        word_counts[word] = 1  

Конечно, вы также можете заранее обработать отсутствующий ключ, используя подход “сначала попробуй, потом поймай” (EAFP):

word_counts = {}  
for word in document:  
    try:  
        word_counts[word] += 1  
    except KeyError:  
        word_counts[word] = 1  

Третий способ — использовать get, который отлично справляется с обработкой отсутствующих ключей:

word_counts = {}  
for word in document:  
    previous_count = word_counts.get(word, 0)  
    word_counts[word] = previous_count + 1  

Словарь по умолчанию (defaultdict) работает так же, как обычный словарь, с одним отличием: когда вы пытаетесь найти несуществующий ключ, он автоматически создает пару ключ-значение, используя предоставленный вами конструктор. Чтобы использовать defaultdict, вам нужно импортировать библиотеку collections:

from collections import defaultdict  
word_counts = defaultdict(int)        # int() генерирует 0  
for word in document:  
    word_counts[word] += 1  

defaultdict также очень полезен со списками, обычными словарями и даже пользовательскими функциями:

dd_list = defaultdict(list)           # list() генерирует пустой список  
dd_list[2].append(1)                  # Теперь dd_list = {2: [1]}  
dd_dict = defaultdict(dict)           # dict() генерирует пустой словарь  
dd_dict["Joel"]["City"] = "Seattle"   # Теперь dd_dict содержит { "Joel" : { "City" : "Seattle"}}  
dd_pair = defaultdict(lambda: [0, 0]) # Создает словарь, где значение для ключа — это список  
dd_pair[2][1] = 1                     # Теперь dd_pair содержит {2: [0,1]}  

Этот метод очень полезен, потому что в будущем, при получении значений по ключу из словаря, нам больше не придется проверять наличие ключа.

Счетчики (Counters)

Счетчик (Counter) может напрямую преобразовывать набор значений в объект, похожий на словарь, где ключом является элемент из набора, а значением — количество его вхождений. Это часто используется при создании гистограмм:

from collections import Counter  
c = Counter([0, 1, 2, 0]) # c (примерно) = { 0 : 2, 1 : 1, 2 : 1 }  

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

word_counts = Counter(document)  

У счетчика также есть часто используемый метод most_common, который напрямую возвращает несколько наиболее часто встречающихся слов и их частоты:

# Вывести 10 наиболее часто встречающихся слов и их счетчики  
for word, count in word_counts.most_common(10):  
    print word, count  

Множества (Sets)

Еще одна структура данных в Python — это множества (sets). Множество — это неупорядоченная коллекция уникальных элементов.
Множество можно создать и добавить в него элементы следующим образом:

s = set()  
s.add(1)          # s = { 1 }  
s.add(2)          # s = { 1, 2 }  
s.add(2)          # s = { 1, 2 }  
x = len(s)        # Равно 2  
y = 2 in s        # Равно True  
z = 3 in s        # Равно False  

Две основные причины использовать множества:

Во-первых, операция in в множествах очень эффективна. Когда количество элементов в наборе данных очень велико, поиск элементов в виде множества явно более подходит, чем в списке:

stopwords_list = ["a","an","at"] + hundreds_of_other_words + ["yet", "you"]  
"zip" in stopwords_list               # Неэффективно, нужно проверять каждый элемент  
stopwords_set = set(stopwords_list)  
"zip" in stopwords_set                # Поиск успешен и очень быстр  

Во-вторых, множества очень удобны для получения уникальных элементов из набора данных:

item_list = [1, 2, 3, 1, 2, 3]  
num_items = len(item_list)            # 6  
item_set = set(item_list)             # {1, 2, 3}  
num_distinct_items = len(item_set)    # 3  
distinct_item_list = list(item_set)   # [1, 2, 3]  

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

Условные операторы

В большинстве языков программирования вы можете использовать if для условных ветвлений следующим образом:

if 1 > 2:  
    message = "если бы только 1 было больше двух…"  
elif 1 > 3:  
    message = "elif означает 'else if'"  
else:  
    message = "когда все остальное не помогает, используйте else (если хотите)"  

Вы также можете написать условное выражение в одну строку, но это редко используется:

parity = "even" if x % 2 == 0 else "odd"  

Циклы

Цикл while

Цикл while в Python:

x = 0  
while x < 10:  
    print x, "меньше 10"  
    x += 1  

Цикл for

Чаще используется цикл for-in:

for x in range(10):  
    print x, "меньше 10"  

Для более сложной логики можно использовать операторы continue и break:

for x in range(10):  
    if x == 3:  
        continue          # перейти к следующей итерации цикла  
    if x == 5:  
        break             # полностью выйти из цикла  
    print x  

В результате будет выведено 0, 1, 2 и 4.

Истинность (Truthiness)

Булевы переменные в Python используются примерно так же, как и в других языках, с той лишь разницей, что первая буква должна быть заглавной:

one_is_less_than_two = 1 < 2      # Равно True  
true_equals_false = True == False # Равно False  

Python использует None для обозначения отсутствия значения, подобно null в других языках:

x = None  
print x == None        # Выводит True, но не самый элегантный способ  
print x is None        # Выводит True, более элегантный способ  

Python позволяет использовать другие значения вместо булевых; следующие эквивалентны False:

Аналогично, существует множество эквивалентов True, что очень удобно для проверки пустых списков, строк, словарей и т.д.

Конечно, если вы не предвидите результат, могут возникнуть ошибки при использовании:

s = some_function_that_returns_a_string()  
if s:  
    first_char = s[0]  
else:  
    first_char = ""  

Более простой подход, который дает тот же эффект, что и выше:

first_char = s and s[0]  

Если первое значение истинно, возвращается второе значение, иначе — первое.

Аналогично, если x может быть числом или None, то так можно получить x, который гарантированно будет числом:

safe_x = x or 0  

В Python также есть функция all, которая возвращает True, если все элементы истинны. Функция any возвращает True, если хотя бы один элемент истинен. Например, для списка, в котором каждый элемент является “истинным”, функция all вернет True, в противном случае — False:

all([True, 1, { 3 }])       # True  
all([True, 1, {}])          # False, {} эквивалентно "False"  
any([True, 1, {}])          # True  
all([])                     # True, нет ни одного элемента, эквивалентного "False"  
any([])                     # False, нет ни одного элемента, эквивалентного "True"  

Для дальнейшего чтения:
Часто используемый синтаксис Python в науке о данных (продвинутый уровень)