#!/usr/bin/env python # coding: utf-8 # # Программирование для всех (основы работы в Python) # # *Алла Тамбовцева, НИУ ВШЭ* # # *Данный ноутбук частично основан на [лекции](http://python.math-hse.info:8080/github/ischurov/pythonhse/blob/master/Lecture%201.ipynb) Щурова И.В., [курс](http://math-info.hse.ru/s15/m) «Программирование на языке Python для сбора и анализа данных» (НИУ ВШЭ).* # ## Вычисления в Python. Переменные. # ### Вычисления в Python # Привычные арифметические действия (сложение, вычитание, умножение, деление) в Python выглядят так же, как и в обычных калькуляторах: # In[1]: 2 + 5 # сложение # In[2]: 2 * 8 - 9 # умножение и вычитание # In[3]: 9 / 2 # деление # Однако с делением всё не так просто: Python 3 всегда будет выдавать результат в виде числа с плавающей точкой (такой тип данных называется *float*), даже тогда, когда ожидается целочисленный ответ. Например: # In[4]: 8 / 2 # не 4 # Получился дробный результат, где дробная часть равна 0. Как быть, если нужен ответ в виде целого числа? Можно воспользоваться целочисленным делением — оператором `//`: # In[5]: 8 // 2 # теперь 4 # Тут важно помнить, что при использовании оператора `//` дробная часть всегда будет просто отбрасываться – никакого округления происходить не будет. Для округления (обычное арифметическое, в большую и в меньшую сторону) существуют специальные функции, и мы их обсудим позже. # In[6]: 9 // 2 # от 4.5 осталось 4 # Что еще можно делать с числами? Возводить в степень и извлекать из них корень. При расчетах на калькуляторе для возведения числа в степень мы обычно используем символ `^`. Попробуем! # In[7]: 9 ^ 2 # Получилось что-то неожиданное. В Python оператор `^` используется для побитного сложения по модулю два (операция интересная, но нам она нигде не понадобится). Для возведения числа в степень потребуется оператор `**`: # In[8]: 9 ** 2 # In[9]: 10 ** 3 # In[10]: 27 ** (1/3) # дробная степень - корень 3 степени # Теперь попробуем извлечь квадратный корень из числа с помощью привычного `sqrt`. # In[11]: sqrt(16) # не получается! # Python пишет, что не знает, что такое `sqrt`. В каких случаях Python может такое писать? Чаще всего в двух случаях: # # * если мы опечатались в названии функции (Python не понимает, что мы от него хотим); # * если мы пытаемся обратиться к функции, которая не является базовой (Python не знает, откуда её брать). # # В данном случае мы столкнулись со второй проблемой. Функция `sqrt()` для вычисления квадратного корня из числа хранится в специальном модуле `math`. Этот модуль стандартный, дополнительно устанавливать его не нужно. Но для того, чтобы воспользоваться этой функцией, нужно сначала импортировать модуль, а потом вызвать из него функцию `sqrt`. Давайте импортируем модуль `math`: # In[12]: import math # А теперь из этого модуля вызовем функцию `sqrt()`: # In[13]: math.sqrt(16) # теперь все работает # Если из `math` нам нужна только одна функция `sqrt` , можно извлечь только её, и тогда прописывать название модуля перед функцией не понадобится: # In[14]: from math import sqrt sqrt(16) # так тоже работает # В `math` есть много полезных функций для вычислений. Чтобы посмотреть, какие функции там есть, после импортирования всего модуля через `import math` можно набрать `math.` и нажать на *Tab* (табуляция, кнопка над *Caps Lock*). Помимо квадратного корня этот модуль поможет вычислять логарифмы, синусы, косинусы и так далее. Остановимся на логарифмах, так как именно операция логарифмирования часто встречается в обработке и анализе данных. # # **Напоминание.** Выражение $\log_{a}(b)$ («логарифм от $b$ по основанию $a$») подразумевает следующий вопрос: в какую степень нужно возвести число $a$, чтобы получить $b$? Например, $\log_{4}(16)=2$, так как $4^2=16$, а $\log_{2}{32}=5$, так как $2^5=32$. # # В анализе данных чаще используются натуральный (по основанию $e\approx2.718$) и десятичный (по основанию 10) логарифмы. Зачем логарифмирование нужно в анализе данных, мы обсудим позже, а пока посмотрим на их вычисление в Python: # In[15]: math.log(27, 3) # логарифм по основанию 3 # In[16]: math.log(12) # натуральный логарифм # In[17]: math.log10(10000) # десятичный логарифм # В модуле `math` также хранятся функции для округления в большую или меньшую сторону: # In[18]: math.ceil(4.2) # от ceil - потолок # In[19]: math.floor(4.6) # от floor - пол # Для обычного округления используется базовая, не встроенная в `math` функция `round()`: # In[20]: round(4.7) # С чем ещё можно столкнуться, выполняя вычисления в Python? С такими вещами: # In[26]: 1 / 18 ** 25 # Результат выше – компьютерная форма экспоненциальной записи числа. Возможно, тот, кто считал что-то на научных или инженерных калькуляторах, уже сталкивался с такой записью. Здесь `e-32` – это $10^{-32}$, а вся запись означает $4.1513310942010236 \cdot 10^{-32}$, то есть примерно $4.15 \cdot 10^{-32}$. Теоретически, если число было очень большим, `e` стояло бы в положительной степени. Но в Python такое не случается, обычно он выводит огромные числа, просто переходя на новую строку, если места на одной не хватает: # In[27]: 23 ** 990 # Компьютерная форма записи числа отчасти помогает понять, почему дробные числа называются числами с плавающей точкой (тип *float*). Возьмем число попроще, например, $12.34$. Его можно записать как $12.34$, как $1.234 \cdot 10$, как $123.4 \cdot 10^{-1}$, $1234 \cdot 10^{-2}$ и так далее. Точка, отделяющая дробную часть от целой, будет «плавать», однако само число при этом меняться не будет, будут меняться только множители – разные степени десятки. # С числами с плавающей точкой связана ещё одна сложность — округление. На первый взгляд, всё хорошо: # In[28]: round(12.6) # округлим до целого - по умолчанию # In[29]: round(12.53, 1) # округлим до первого знака после запятой # С другой стороны, могут возникнуть странности: # In[30]: round(2.50) # не 3 # In[31]: round(3.525, 2) # не 3.53 # Эти странности связаны с тем, что число, которое мы видим (например, 3.525), не совпадает с тем, которое хранится в компьютере, потому что оно при сохранении преобразовывается и превращается из точного 3.525 в такое: # In[32]: from decimal import Decimal Decimal(3.525) # И такое число будет законно округляться до 3.52 по правилам арифметического округления. В прикладном анализе данных такие сложности редко вызывают проблемы, но знать про нее полезно, чтобы не пугаться и не удивляться неожиданным результатам. Кроме того, полезно помнить, что числа с плавающей точкой (типа *float*) не рекомендуется использовать в финансовых вычислениях и вообще в вычислениях, требующих высокой точности, поскольку они «накапливают ошибку», то есть дают неточные результаты. # ### Переменные # Переменные в программировании похожи на переменные в математике. Кроме того, их можно рассматривать как хранилища значений – контейнеры или «коробки», в которые мы что-то кладем. Python, в отличие от некоторых языков программирования (C, C++, Java), сам распознает что мы «кладем в коробку»: число, целое число, текст, список чисел... Поэтому при создании переменной нам не нужно указывать ее тип. # In[33]: x = 2 # Python поймет, что это целые числа y = 3 # In[34]: print(x) print(y) # Значения переменных мы можем обновлять – изменить значение и сохранить в переменную с тем же названием. # In[35]: x = x + 1 # возьмем значение x, увеличим на 1 и сохраним изменения в переменной x # In[36]: y = y * 2 # возьмем значение y, увеличим в 2 раза и сохраним изменения # In[37]: print(x, y) # одновременно два значения # Названия переменных в Python могут быть почти любыми. Три общих правила: название переменной не должно начинаться с цифры, в названии не должно быть пробелов, название не должно совпадать со служебными (зарезервированными) словами в Python. Список зарезервированных слов можно узнать так: # In[38]: import keyword print(keyword.kwlist) # Обычно рекомендуется давать переменным осмысленные названия: если речь идёт о доходе, называть переменную не `x`, а `income`, если речь идёт о данных по преступности, сохранять таблицу в переменную `crimes`, и так далее. Технически, Python 3 допускает названия на кириллице, но это будет выглядеть странно и неуместно. # Рассмотрим такую задачу. Пришла весна и решили мы заняться бегом по такой схеме: каждый день мы пробегаем столько, сколько в сумме за два предыдущих дня. При этом первые два дня мы морально готовимся: топчемся на месте и символически проходим по одному метру (полшага назад и полшага вперед). Если мы будем записывать все пройденные нами расстояния в ряд, мы получим последовательность из [чисел Фибоначчи](https://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D0%A4%D0%B8%D0%B1%D0%BE%D0%BD%D0%B0%D1%87%D1%87%D0%B8). Давайте напишем код, который будет считать, сколько метров мы будем пробегать в следующий день. # Сначала создадим переменные, в которые сохраним данные по первым двум дням. # In[39]: b = 1 # день 1 - морально готовимся бегать, «бежим» 1 метр i = 1 # номер дня, когда начинаем бегать bnext = 1 # день 2 - снова морально готовимся бегать, «бежим» 1 метр i = i + 1 # перешли ко второму дню, увеличили i на 1 # In[40]: res = b + bnext # в следующий день пробегаем столько же, сколько за два предыдущих i = i + 1 # перешли к следующему дню, увеличили i на 1 b = bnext # значение b нам уже не нужно, сдвигаемся к следующему дню - записываем bnext bnext = res # запомнили полученное значение res print(i, bnext) # выводим на экран номер дня и расстояние, которое нужно пробежать # Теперь можно прогонять предыдущую ячейку много раз (через *Ctrl + Enter*) и получать результат по каждому дню. Например, на 20 день мы будем пробегать уже нормальное расстояние — 6765 метров, почти 7 километров. Конечно, прогонять одну и ту ячейку много раз неудобно и странно, но о том, как считать числа Фибоначчи более рационально, мы поговорим, когда будем разбирать циклы. # # **Важно:** если бы не разбили наш код на части (на две ячейки), ничего бы при повторном запуске ячейки не произошло — переменным `b`, `bnext` и `i` заново присваивались бы значения 1, и движения вперед бы не происходило. # # **Примечание:** можно было последней строкой написать `print(i, res)`, ничего бы не изменилось.