Pandas, Анализ данных

SQL и PANDAS

Время прочтения: 6 мин.

PANDAS — это библиотека на языке Python, созданная для анализа и обработки данных. Имеет открытый исходный код и поддерживается разработчиками Anaconda. Эта библиотека хорошо подходит для структурированных (табличных) данных.

Для начала импортируем библиотеки, которые пригодятся нам по ходу работы:

import pandas as pd
import numpy as np

И для наглядности возьмём csv таблицу:

Далее мы записываем информацию из csv в DataFrame, который назовем test_csv, и убедимся, что полученная таблица будет иметь тоже имя и структуру, как и оригинальный csv:

Ввод: test_csv = pd.read_csv('test.csv')
      test_csv.head()
Вывод: 
   ID  FIO             ID_STATUS   OPEN        CLOSE       PRICE   SALE
0  45  Горбунов В.Ф.   В работе    02/05/2020  NaN         1500    0.8
1  49  Нестерова В.В.  В работе    02/05/2020  NaN         2300    0.9
2  52  Горбунов В.Ф.   Выполнено   02/05/2020  04/05/2020  3500    1
3  54  Нестерова В.В.  В работе    03/05/2020  NaN         750     0.6
4  55  Мамонтов Б.А.   Выполнено   03/05/2020  06/05/2020  1230    0.95

SELECT

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

SELECT ID, ID_STATUS, CLOSE
FROM test_csv
LIMIT 5;

В Pandas выбор столбцов происходит с помощью перечисления необходимых названий столбцов в списке в нашем DataFrame:

Ввод: test_csv[['ID', 'ID_STATUS', 'CLOSE']].head(5)
Вывод:  
   ID  ID_STATUS   CLOSE      
0  45  В работе    NaN         
1  49  В работе    NaN        
2  52  Выполнено   04/05/2020  
3  54  В работе    NaN         
4  55  Выполнено   06/05/2020  

А если же мы вызываем DataFrame без листа с названиями столбцов, то это отобразить все столбцы словно * в SQL.

В SQL мы сразу можем добавить столбец с нужными нам расчетами:

SELECT *, PRICE*SALE as SUM
FROM test_csv
LIMIT 5;

В Pandas  для добавления столбца с расчетами мы воспользуемся DataFrame.assign():

Ввод: test_csv.assign(SUM=test_csv['PRICE'] / test_csv['SALE']).head(5)
Вывод: 
   ID  FIO             ID_STATUS   OPEN        CLOSE       PRICE   SALE   SUM
0  45  Горбунов В.Ф.   В работе    02/05/2020  NaN         1500    0.8    1200
1  49  Нестерова В.В.  В работе    02/05/2020  NaN         2300    0.9    2070
2  52  Горбунов В.Ф.   Выполнено   02/05/2020  04/05/2020  3500    1      3500
3  54  Нестерова В.В.  В работе    03/05/2020  NaN         750     0.6    450
4  55  Мамонтов Б.А.   Выполнено   03/05/2020  06/05/2020  1230    0.95   1168.5

WHERE

Фильтрация в SQL происходит при помощи WHERE:

SELECT *
FROM test_csv
WHERE ID_STATUS = 'Выполнено'
LIMIT 3;

DataFrame же может быть отфильтрован несколькими способами, но самыми частым из них является логическое сравнение:

Ввод: test_csv[test_csv['ID_STATUS'] == 'Выполнено'].head(3)
Вывод: 
   ID  FIO             ID_STATUS   OPEN        CLOSE       PRICE   SALE
1  52  Горбунов В.Ф.   Выполнено   02/05/2020  04/05/2020  3500    1
2  55  Мамонтов Б.А.   Выполнено   03/05/2020  06/05/2020  1230    0.95
3  56  Горбунов В.Ф.   Выполнено   03/05/2020  07/05/2020  767     0.35

Также, как и в SQL, в DataFrame мы можем использовать операторы И/ИЛИ:

SELECT *
FROM test_csv
WHERE ID_STATUS = 'Выполнено' AND PRICE > 1000;

Ввод: test_csv[(test_csv['ID_STATUS'] == 'Выполнено') & (test_csv['PRICE'] > 1000)]
Вывод: 
   ID  FIO             ID_STATUS   OPEN        CLOSE       PRICE   SALE
0  52  Горбунов В.Ф.   Выполнено   02/05/2020  04/05/2020  3500    1
1  55  Мамонтов Б.А.   Выполнено   03/05/2020  06/05/2020  1230    0.95

Для проверки наличия в значении NULL, мы используем notna() и isna(). Для примера создадим DataFrame с NULL значениями.

Ввод: test_1 = pd.DataFrame({'C1': ['1', '1', np.NaN, '1', '1'],
                             'C2': ['2', np.NaN, '2', '2', np.NaN],
                             'C3': [np.NaN, '3', '3', '3', np.NaN]})
      test_1
Вывод: 
    C1   C2   С3
0    1    2  NaN 
1    1  NaN    3
2  NaN    2    3
3    1    2    3
4    1  NaN  NaN

И теперь для примера выберем все строки, где С2 IS NULL:

SELECT *
FROM test_1
WHERE C2 IS NULL;
Ввод: test_1[test_1['C2'].isna()]
Вывод: 
    C1   C2   С3
0    1  NaN    3
1    1  NaN  NaN

Для получения IS NOT NULL значений по столбцу С3 воспользуемся notna():

SELECT *
FROM test_1
WHERE C3 IS NOT NULL;
Ввод: test_1[test_1['C3'].notna()]
Вывод: 
    C1   C2   С3
0    1  NaN    3
1  NaN    2    3
2    1    2    3

UNION

UNION ALL в PANDAS осуществляется с помощью concat():

Ввод: set_1 = pd.DataFrame({'name': ['Банан', 'Арбуз', 'Яблоко'],
                            'price': [90, 30, 150]})
      set_2 = pd.DataFrame({'name': ['Арбуз', 'Ананас', 'Груша'],
                            ''price': [30, 190, 80]})

SELECT name, price
FROM set_1
UNION ALL
SELECT name, price
FROM set_2;
/*
         name  price
        Банан     90
        Арбуз     30
       Яблоко    150
        Арбуз     30
       Ананас    190
        Груша     80
*/
Ввод: pd.concat([set_1, set_2])
Вывод: 
         name  price
0       Банан     90
1       Арбуз     30
2      Яблоко    150
3       Арбуз     30
4      Ананас    190
5       Груша     80

UNION из SQL похожа по функционалу на UNION ALL с отличием только в том, что UNION удаляет дубликаты строк.

SELECT name, price
FROM set_1
UNION
SELECT name, price
FROM set_2;
/*
         name  price
        Банан     90
        Арбуз     30
       Яблоко    150
       Ананас    190
        Груша     80
*/

В PANDAS мы можем использовать concat() в сочетании с drop_duplicates():

Ввод: pd.concat([set_1, set_2]).drop_duplicates()
Вывод: 
         name  price
0       Банан     90
1       Арбуз     30
2      Яблоко    150
3      Ананас    190
4       Груша     80

UPDATE

С помощью UPDATE мы можем “обновить” значения:

SQL:
UPDATE test_csv
SET SALE = SALE*0.9
WHERE SALE > 0.5;
PYTHON:
test_csv.loc[test_csv['SALE'] > 0.5, 'SALE'] *= 0.9

DELETE

В SQL удаление с условием выглядит так:

DELETE FROM test_csv
WHERE SALE > 0.75;

В PANDAS же мы выбираем какие столбцы остаются, а не удаляются как это сделано в SQL:

Ввод: test_csv = test_csv.loc[test_csv['SALE'] <= 0.75]

ЗАДАЧИ

Так как мы рассмотрели основные функции SQL и PANDAS на примерах, то попробуем решить пару задачек, с которыми мы можем столкнуться в повседневной работе.

Пусть у нас имеется csv таблица work:

  • ID – ID работника
  • FIO – ФИО работника
  • DEPT – Отдел
  • CHIED_ID – Непосредственный руководитель
  • SALARY – Заработная плата

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

В SQL это будет выглядит так:

SELECT a.*
FROM   work a
WHERE  a.SALARY = ( SELECT MAX(SALARY) 
                    FROM work b
                    WHERE  b.DEPT = a.DEPT )

Для python одним из вариантов будет:

import pandas as pd
import numpy as np

work_csv = pd.read_csv('work.csv', header=0)
unique_dept = pd.unique(df['DEPT']).tolist()
for ud in unique_dept:
   ud_df = work_csv[(work_csv['DEPT'] == ud)]
   max_salary.append(ud_df.iloc[ud_df['SALARY'.idxmax()])
print(max_salary)

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

В следующей задаче нам нужно вывести список ID отделов, где количество сотрудников не превышает трех человек.

В SQL это будет выглядит так:

SELECT DEPT
FROM work
GROUP BY DEPT
HAVING COUNT(*) <= 3

Для python одним из вариантов будет:

import pandas as pd
import numpy as np

work_csv = pd.read_csv('work.csv', header=0)
unique_dept = pd.unique(df['DEPT']).tolist()
for ud in unique_dept:
   if len(work_csv[(work_csv['DEPT'] == ud)] <= 3:
      print(ud)

И для данной задачи мы получим ответ: 0 и 2, т.к. только они и удовлетворяют условиям нашей задачи.

Мы рассмотрели основные функции SQL в рамках PANDAS на примерах, закрепили полученные знания на практике и теперь с уверенностью можем покорять новые горизонты!

Советуем почитать