Сравнение с SAS#
Для потенциальных пользователей, знакомых с SAS Эта страница предназначена для демонстрации того, как различные операции SAS выполняются в pandas.
Если вы новичок в pandas, возможно, вам сначала стоит прочитать 10 минут до pandas чтобы ознакомиться с библиотекой.
Как принято, мы импортируем pandas и NumPy следующим образом:
In [1]: import pandas as pd
In [2]: import numpy as np
Структуры данных#
Общий перевод терминологии#
pandas |
SAS |
|---|---|
|
набор данных |
столбец |
переменная |
строка |
наблюдение |
groupby |
BY-group |
|
|
DataFrame#
A DataFrame в pandas аналогичен набору данных SAS - двумерному источнику данных с помеченными столбцами, которые могут быть разных типов. Как будет показано в этом документе, почти любую операцию, которую можно применить к набору данных с использованием DATA этап также может быть выполнен в pandas.
Series#
A Series это структура данных, представляющая один столбец
DataFrame. SAS не имеет отдельной структуры данных для одного столбца,
но в целом, работа с Series аналогично ссылке на столбец
в DATA шаг.
Index#
Каждый DataFrame и Series имеет Index - которые являются метками на
строки данных. SAS не имеет точно аналогичной концепции. Строки набора данных по сути не имеют меток, кроме неявного целочисленного индекса, который можно получить во время DATA шаг (_N_).
В pandas, если индекс не указан, по умолчанию также используется целочисленный индекс
(первая строка = 0, вторая строка = 1 и так далее). При использовании помеченного Index или
MultiIndex может обеспечить сложный анализ и в конечном итоге является важной
частью pandas для понимания, но для этого сравнения мы в основном проигнорируем
Index и просто рассматривать DataFrame как коллекцию столбцов. Пожалуйста,
см. документация по индексированию для получения дополнительной информации о том, как использовать
Index эффективно.
Копии vs. операции на месте#
Большинство операций pandas возвращают копии Series/DataFrame. Чтобы изменения "сохранились",
вам нужно либо присвоить новой переменной:
sorted_df = df.sort_values("col1")
или перезаписать исходный:
df = df.sort_values("col1")
Примечание
Вы увидите inplace=True или copy=False аргумент ключевого слова доступен для
некоторых методов:
df.replace(5, inplace=True)
Идет активное обсуждение об устаревании и удалении inplace и copy для
большинства методов (например, dropna) за исключением очень небольшого подмножества методов
(включая replace). Оба ключевых слова больше не будут необходимы в контексте Copy-on-Write. Предложение можно найти
здесь.
Ввод / вывод данных#
Создание DataFrame из значений#
Набор данных SAS может быть создан из указанных значений путем
размещения данных после datalines оператор и
указание имен столбцов.
data df;
input x y;
datalines;
1 2
3 4
5 6
;
run;
Pandas DataFrame может быть построен множеством различных способов,
но для небольшого количества значений часто удобно задавать его как
словарь Python, где ключи - это имена столбцов,
а значения - данные.
In [1]: df = pd.DataFrame({"x": [1, 3, 5], "y": [2, 4, 6]})
In [2]: df
Out[2]:
x y
0 1 2
1 3 4
2 5 6
Чтение внешних данных#
Как и SAS, pandas предоставляет утилиты для чтения данных из
многих форматов. The tips набор данных, находящийся в тестах pandas (csv)
будет использоваться во многих следующих примерах.
SAS предоставляет PROC IMPORT для чтения csv-данных в набор данных.
proc import datafile='tips.csv' dbms=csv out=tips replace;
getnames=yes;
run;
Метод pandas - это read_csv(), который работает аналогичным образом.
In [3]: url = (
...: "https://raw.githubusercontent.com/pandas-dev/"
...: "pandas/main/pandas/tests/io/data/csv/tips.csv"
...: )
...:
In [4]: tips = pd.read_csv(url)
In [5]: tips
Out[5]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 29.03 5.92 Male No Sat Dinner 3
240 27.18 2.00 Female Yes Sat Dinner 2
241 22.67 2.00 Male Yes Sat Dinner 2
242 17.82 1.75 Male No Sat Dinner 2
243 18.78 3.00 Female No Thur Dinner 2
[244 rows x 7 columns]
Как PROC IMPORT, read_csv может принимать ряд параметров для указания
того, как должны быть разобраны данные. Например, если данные разделены табуляцией
и не имеют имён столбцов, команда pandas будет:
tips = pd.read_csv("tips.csv", sep="\t", header=None)
# alternatively, read_table is an alias to read_csv with tab delimiter
tips = pd.read_table("tips.csv", header=None)
В дополнение к тексту/csv, pandas поддерживает различные другие форматы данных,
такие как Excel, HDF5 и базы данных SQL. Все они читаются через pd.read_*
функции. См. Документация по вводу-выводу для получения дополнительной информации.
Ограничение вывода#
По умолчанию pandas будет обрезать вывод больших DataFrames для отображения первых и последних строк.
Это можно переопределить с помощью изменение параметров pandas, или используя
DataFrame.head() или DataFrame.tail().
In [1]: tips.head(5)
Out[1]:
total_bill tip sex smoker day time size
0 16.99 1.01 Female No Sun Dinner 2
1 10.34 1.66 Male No Sun Dinner 3
2 21.01 3.50 Male No Sun Dinner 3
3 23.68 3.31 Male No Sun Dinner 2
4 24.59 3.61 Female No Sun Dinner 4
Эквивалент в SAS будет:
proc print data=df(obs=5);
run;
Экспорт данных#
Обратная величина PROC IMPORT в SAS это PROC EXPORT
proc export data=tips outfile='tips2.csv' dbms=csv;
run;
Аналогично в pandas, противоположностью read_csv является to_csv(),
и другие форматы данных следуют аналогичному API.
tips.to_csv("tips2.csv")
Операции с данными#
Операции со столбцами#
В DATA шаг, произвольные математические выражения могут
использоваться для новых или существующих столбцов.
data tips;
set tips;
total_bill = total_bill - 2;
new_bill = total_bill / 2;
run;
pandas предоставляет векторизованные операции путем указания отдельных Series в
DataFrameНовые столбцы могут быть назначены аналогичным образом. DataFrame.drop() метод удаляет
столбец из DataFrame.
In [1]: tips["total_bill"] = tips["total_bill"] - 2
In [2]: tips["new_bill"] = tips["total_bill"] / 2
In [3]: tips
Out[3]:
total_bill tip sex smoker day time size new_bill
0 14.99 1.01 Female No Sun Dinner 2 7.495
1 8.34 1.66 Male No Sun Dinner 3 4.170
2 19.01 3.50 Male No Sun Dinner 3 9.505
3 21.68 3.31 Male No Sun Dinner 2 10.840
4 22.59 3.61 Female No Sun Dinner 4 11.295
.. ... ... ... ... ... ... ... ...
239 27.03 5.92 Male No Sat Dinner 3 13.515
240 25.18 2.00 Female Yes Sat Dinner 2 12.590
241 20.67 2.00 Male Yes Sat Dinner 2 10.335
242 15.82 1.75 Male No Sat Dinner 2 7.910
243 16.78 3.00 Female No Thur Dinner 2 8.390
[244 rows x 8 columns]
In [4]: tips = tips.drop("new_bill", axis=1)
Фильтрация#
Фильтрация в SAS выполняется с помощью if или where выражение, по одному
или нескольким столбцам.
data tips;
set tips;
if total_bill > 10;
run;
data tips;
set tips;
where total_bill > 10;
/* equivalent in this case - where happens before the
DATA step begins and can also be used in PROC statements */
run;
DataFrames могут быть отфильтрованы несколькими способами; наиболее интуитивным из которых является использование булевую индексацию.
In [1]: tips[tips["total_bill"] > 10]
Out[1]:
total_bill tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
5 23.29 4.71 Male No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 27.03 5.92 Male No Sat Dinner 3
240 25.18 2.00 Female Yes Sat Dinner 2
241 20.67 2.00 Male Yes Sat Dinner 2
242 15.82 1.75 Male No Sat Dinner 2
243 16.78 3.00 Female No Thur Dinner 2
[204 rows x 7 columns]
Приведённое выражение просто передаёт Series of True/False объекты в DataFrame,
возвращая все строки с True.
In [2]: is_dinner = tips["time"] == "Dinner"
In [3]: is_dinner
Out[3]:
0 True
1 True
2 True
3 True
4 True
...
239 True
240 True
241 True
242 True
243 True
Name: time, Length: 244, dtype: bool
In [4]: is_dinner.value_counts()
Out[4]:
time
True 176
False 68
Name: count, dtype: int64
In [5]: tips[is_dinner]
Out[5]:
total_bill tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
1 8.34 1.66 Male No Sun Dinner 3
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 27.03 5.92 Male No Sat Dinner 3
240 25.18 2.00 Female Yes Sat Dinner 2
241 20.67 2.00 Male Yes Sat Dinner 2
242 15.82 1.75 Male No Sat Dinner 2
243 16.78 3.00 Female No Thur Dinner 2
[176 rows x 7 columns]
Логика if/then#
В SAS логика if/then может использоваться для создания новых столбцов.
data tips;
set tips;
format bucket $4.;
if total_bill < 10 then bucket = 'low';
else bucket = 'high';
run;
Та же операция в pandas может быть выполнена с использованием where метод из numpy.
In [1]: tips["bucket"] = np.where(tips["total_bill"] < 10, "low", "high")
In [2]: tips
Out[2]:
total_bill tip sex smoker day time size bucket
0 14.99 1.01 Female No Sun Dinner 2 high
1 8.34 1.66 Male No Sun Dinner 3 low
2 19.01 3.50 Male No Sun Dinner 3 high
3 21.68 3.31 Male No Sun Dinner 2 high
4 22.59 3.61 Female No Sun Dinner 4 high
.. ... ... ... ... ... ... ... ...
239 27.03 5.92 Male No Sat Dinner 3 high
240 25.18 2.00 Female Yes Sat Dinner 2 high
241 20.67 2.00 Male Yes Sat Dinner 2 high
242 15.82 1.75 Male No Sat Dinner 2 high
243 16.78 3.00 Female No Thur Dinner 2 high
[244 rows x 8 columns]
Функциональность дат#
SAS предоставляет различные функции для операций со столбцами даты/времени.
data tips;
set tips;
format date1 date2 date1_plusmonth mmddyy10.;
date1 = mdy(1, 15, 2013);
date2 = mdy(2, 15, 2015);
date1_year = year(date1);
date2_month = month(date2);
* shift date to beginning of next interval;
date1_next = intnx('MONTH', date1, 1);
* count intervals between dates;
months_between = intck('MONTH', date1, date2);
run;
Эквивалентные операции pandas показаны ниже. В дополнение к этим функциям pandas поддерживает другие возможности временных рядов, недоступные в Base SAS (такие как ресемплинг и пользовательские смещения) - см. документация по временным рядам для получения дополнительной информации.
In [1]: tips["date1"] = pd.Timestamp("2013-01-15")
In [2]: tips["date2"] = pd.Timestamp("2015-02-15")
In [3]: tips["date1_year"] = tips["date1"].dt.year
In [4]: tips["date2_month"] = tips["date2"].dt.month
In [5]: tips["date1_next"] = tips["date1"] + pd.offsets.MonthBegin()
In [6]: tips["months_between"] = tips["date2"].dt.to_period("M") - tips[
...: "date1"
...: ].dt.to_period("M")
...:
In [7]: tips[
...: ["date1", "date2", "date1_year", "date2_month", "date1_next", "months_between"]
...: ]
...:
Out[7]:
date1 date2 date1_year date2_month date1_next months_between
0 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
1 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
2 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
3 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
4 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
.. ... ... ... ... ... ...
239 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
240 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
241 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
242 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
243 2013-01-15 2015-02-15 2013 2 2013-02-01 <25 * MonthEnds>
[244 rows x 6 columns]
Выбор столбцов#
SAS предоставляет ключевые слова в DATA шаг для выбора,
удаления и переименования столбцов.
data tips;
set tips;
keep sex total_bill tip;
run;
data tips;
set tips;
drop sex;
run;
data tips;
set tips;
rename total_bill=total_bill_2;
run;
Те же операции выражены в pandas ниже.
Оставить определенные столбцы#
In [1]: tips[["sex", "total_bill", "tip"]]
Out[1]:
sex total_bill tip
0 Female 14.99 1.01
1 Male 8.34 1.66
2 Male 19.01 3.50
3 Male 21.68 3.31
4 Female 22.59 3.61
.. ... ... ...
239 Male 27.03 5.92
240 Female 25.18 2.00
241 Male 20.67 2.00
242 Male 15.82 1.75
243 Female 16.78 3.00
[244 rows x 3 columns]
Удалить столбец#
In [2]: tips.drop("sex", axis=1)
Out[2]:
total_bill tip smoker day time size
0 14.99 1.01 No Sun Dinner 2
1 8.34 1.66 No Sun Dinner 3
2 19.01 3.50 No Sun Dinner 3
3 21.68 3.31 No Sun Dinner 2
4 22.59 3.61 No Sun Dinner 4
.. ... ... ... ... ... ...
239 27.03 5.92 No Sat Dinner 3
240 25.18 2.00 Yes Sat Dinner 2
241 20.67 2.00 Yes Sat Dinner 2
242 15.82 1.75 No Sat Dinner 2
243 16.78 3.00 No Thur Dinner 2
[244 rows x 6 columns]
Переименовать столбец#
In [3]: tips.rename(columns={"total_bill": "total_bill_2"})
Out[3]:
total_bill_2 tip sex smoker day time size
0 14.99 1.01 Female No Sun Dinner 2
1 8.34 1.66 Male No Sun Dinner 3
2 19.01 3.50 Male No Sun Dinner 3
3 21.68 3.31 Male No Sun Dinner 2
4 22.59 3.61 Female No Sun Dinner 4
.. ... ... ... ... ... ... ...
239 27.03 5.92 Male No Sat Dinner 3
240 25.18 2.00 Female Yes Sat Dinner 2
241 20.67 2.00 Male Yes Sat Dinner 2
242 15.82 1.75 Male No Sat Dinner 2
243 16.78 3.00 Female No Thur Dinner 2
[244 rows x 7 columns]
Сортировка по значениям#
Сортировка в SAS выполняется через PROC SORT
proc sort data=tips;
by sex total_bill;
run;
pandas имеет DataFrame.sort_values() метода, который принимает список столбцов для сортировки.
In [1]: tips = tips.sort_values(["sex", "total_bill"])
In [2]: tips
Out[2]:
total_bill tip sex smoker day time size
67 1.07 1.00 Female Yes Sat Dinner 1
92 3.75 1.00 Female Yes Fri Dinner 2
111 5.25 1.00 Female No Sat Dinner 1
145 6.35 1.50 Female No Thur Lunch 2
135 6.51 1.25 Female No Thur Lunch 2
.. ... ... ... ... ... ... ...
182 43.35 3.50 Male Yes Sun Dinner 3
156 46.17 5.00 Male No Sun Dinner 6
59 46.27 6.73 Male No Sat Dinner 4
212 46.33 9.00 Male No Sat Dinner 4
170 48.81 10.00 Male Yes Sat Dinner 3
[244 rows x 7 columns]
Обработка строк#
Определение длины строки#
SAS определяет длину строки символов с помощью
ДЛИНА
и LENGTHC
функций. LENGTHN исключает завершающие пробелы и LENGTHC включает завершающие пробелы.
data _null_;
set tips;
put(LENGTHN(time));
put(LENGTHC(time));
run;
Вы можете найти длину строки символов с помощью Series.str.len().
В Python 3 все строки являются строками Unicode. len включает завершающие пробелы.
Используйте len и rstrip для исключения завершающих пробелов.
In [1]: tips["time"].str.len()
Out[1]:
67 6
92 6
111 6
145 5
135 5
..
182 6
156 6
59 6
212 6
170 6
Name: time, Length: 244, dtype: int64
In [2]: tips["time"].str.rstrip().str.len()
Out[2]:
67 6
92 6
111 6
145 5
135 5
..
182 6
156 6
59 6
212 6
170 6
Name: time, Length: 244, dtype: int64
Поиск позиции подстроки#
SAS определяет позицию символа в строке с помощью
FINDW функция.
FINDW принимает строку, определенную первым аргументом, и ищет первую позицию подстроки,
которую вы указываете в качестве второго аргумента.
data _null_;
set tips;
put(FINDW(sex,'ale'));
run;
Вы можете найти позицию символа в столбце строк с помощью Series.str.find()
метод. find ищет первую позицию подстроки. Если подстрока найдена, метод возвращает её позицию. Если не найдена, возвращает -1Имейте в виду, что индексы в Python начинаются с нуля.
In [1]: tips["sex"].str.find("ale")
Out[1]:
67 3
92 3
111 3
145 3
135 3
..
182 1
156 1
59 1
212 1
170 1
Name: sex, Length: 244, dtype: int64
Извлечение подстроки по позиции#
SAS извлекает подстроку из строки на основе её позиции с помощью SUBSTR функция.
data _null_;
set tips;
put(substr(sex,1,1));
run;
С pandas вы можете использовать [] нотация для извлечения подстроки
из строки по позициям. Помните, что в Python
индексация начинается с нуля.
In [1]: tips["sex"].str[0:1]
Out[1]:
67 F
92 F
111 F
145 F
135 F
..
182 M
156 M
59 M
212 M
170 M
Name: sex, Length: 244, dtype: object
Извлечение n-го слова#
SAS SCAN функция возвращает n-ное слово из строки. Первый аргумент — строка, которую нужно разобрать, а второй аргумент указывает, какое слово нужно извлечь.
data firstlast;
input String $60.;
First_Name = scan(string, 1);
Last_Name = scan(string, -1);
datalines2;
John Smith;
Jane Cook;
;;;
run;
Самый простой способ извлечь слова в pandas — разделить строки по пробелам, затем обратиться к слову по индексу. Обратите внимание, что существуют более мощные подходы, если они вам понадобятся.
In [1]: firstlast = pd.DataFrame({"String": ["John Smith", "Jane Cook"]})
In [2]: firstlast["First_Name"] = firstlast["String"].str.split(" ", expand=True)[0]
In [3]: firstlast["Last_Name"] = firstlast["String"].str.rsplit(" ", expand=True)[1]
In [4]: firstlast
Out[4]:
String First_Name Last_Name
0 John Smith John Smith
1 Jane Cook Jane Cook
Изменение регистра#
SAS UPCASE LOWCASE и PROPCASE функции изменяют регистр аргумента.
data firstlast;
input String $60.;
string_up = UPCASE(string);
string_low = LOWCASE(string);
string_prop = PROPCASE(string);
datalines2;
John Smith;
Jane Cook;
;;;
run;
Эквивалентные методы pandas: Series.str.upper(), Series.str.lower(), и
Series.str.title().
In [1]: firstlast = pd.DataFrame({"string": ["John Smith", "Jane Cook"]})
In [2]: firstlast["upper"] = firstlast["string"].str.upper()
In [3]: firstlast["lower"] = firstlast["string"].str.lower()
In [4]: firstlast["title"] = firstlast["string"].str.title()
In [5]: firstlast
Out[5]:
string upper lower title
0 John Smith JOHN SMITH john smith John Smith
1 Jane Cook JANE COOK jane cook Jane Cook
Слияние#
Следующие таблицы будут использоваться в примерах слияния:
In [1]: df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value": np.random.randn(4)})
In [2]: df1
Out[2]:
key value
0 A 0.469112
1 B -0.282863
2 C -1.509059
3 D -1.135632
In [3]: df2 = pd.DataFrame({"key": ["B", "D", "D", "E"], "value": np.random.randn(4)})
In [4]: df2
Out[4]:
key value
0 B 1.212112
1 D -0.173215
2 D 0.119209
3 E -1.044236
В SAS данные должны быть явно отсортированы перед слиянием. Различные типы соединений выполняются с использованием in= фиктивные переменные для отслеживания, было ли найдено совпадение в одном или обоих входных фреймах.
proc sort data=df1;
by key;
run;
proc sort data=df2;
by key;
run;
data left_join inner_join right_join outer_join;
merge df1(in=a) df2(in=b);
if a and b then output inner_join;
if a then output left_join;
if b then output right_join;
if a or b then output outer_join;
run;
pandas DataFrames имеют merge() метод, который предоставляет аналогичную функциональность.
Данные не нужно предварительно сортировать, а различные типы объединений выполняются с помощью
how ключевое слово.
In [1]: inner_join = df1.merge(df2, on=["key"], how="inner")
In [2]: inner_join
Out[2]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
In [3]: left_join = df1.merge(df2, on=["key"], how="left")
In [4]: left_join
Out[4]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
In [5]: right_join = df1.merge(df2, on=["key"], how="right")
In [6]: right_join
Out[6]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209
3 E NaN -1.044236
In [7]: outer_join = df1.merge(df2, on=["key"], how="outer")
In [8]: outer_join
Out[8]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
Отсутствующие данные#
Как pandas, так и SAS имеют представление для пропущенных данных.
pandas представляет отсутствующие данные специальным значением с плавающей точкой NaN (не число). Многие семантики совпадают; например, пропущенные данные распространяются через числовые операции и по умолчанию игнорируются при агрегациях.
In [1]: outer_join
Out[1]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E NaN -1.044236
In [2]: outer_join["value_x"] + outer_join["value_y"]
Out[2]:
0 NaN
1 0.929249
2 NaN
3 -1.308847
4 -1.016424
5 NaN
dtype: float64
In [3]: outer_join["value_x"].sum()
Out[3]: -3.5940742896293765
Одно отличие заключается в том, что пропущенные данные нельзя сравнивать с их сторожевой меткой. Например, в SAS вы могли бы сделать это для фильтрации пропущенных значений.
data outer_join_nulls;
set outer_join;
if value_x = .;
run;
data outer_join_no_nulls;
set outer_join;
if value_x ^= .;
run;
В pandas, Series.isna() и Series.notna() можно использовать для фильтрации строк.
In [1]: outer_join[outer_join["value_x"].isna()]
Out[1]:
key value_x value_y
5 E NaN -1.044236
In [2]: outer_join[outer_join["value_x"].notna()]
Out[2]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 NaN
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
pandas предоставляет различные методы работы с пропущенными данными. Вот несколько примеров:
Удаление строк с пропущенными значениями#
In [3]: outer_join.dropna()
Out[3]:
key value_x value_y
1 B -0.282863 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
Заполнение вперед из предыдущих строк#
In [4]: outer_join.ffill()
Out[4]:
key value_x value_y
0 A 0.469112 NaN
1 B -0.282863 1.212112
2 C -1.509059 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209
5 E -1.135632 -1.044236
Замена пропущенных значений указанным значением#
Использование среднего значения:
In [5]: outer_join["value_x"].fillna(outer_join["value_x"].mean())
Out[5]:
0 0.469112
1 -0.282863
2 -1.509059
3 -1.135632
4 -1.135632
5 -0.718815
Name: value_x, dtype: float64
GroupBy#
Агрегация#
SAS’s PROC SUMMARY может использоваться для группировки по одной или
нескольким ключевым переменным и вычисления агрегаций по
числовым столбцам.
proc summary data=tips nway;
class sex smoker;
var total_bill tip;
output out=tips_summed sum=;
run;
pandas предоставляет гибкий groupby механизм, позволяющий выполнять аналогичные агрегации. См.
документация по groupby для получения дополнительной информации и примеров.
In [1]: tips_summed = tips.groupby(["sex", "smoker"])[["total_bill", "tip"]].sum()
In [2]: tips_summed
Out[2]:
total_bill tip
sex smoker
Female No 869.68 149.77
Yes 527.27 96.74
Male No 1725.75 302.00
Yes 1217.07 183.07
Преобразование#
В SAS, если агрегации по группам нужно использовать с исходным фреймом, их необходимо объединить обратно. Например, чтобы вычесть среднее значение для каждого наблюдения по группам курящих.
proc summary data=tips missing nway;
class smoker;
var total_bill;
output out=smoker_means mean(total_bill)=group_bill;
run;
proc sort data=tips;
by smoker;
run;
data tips;
merge tips(in=a) smoker_means(in=b);
by smoker;
adj_total_bill = total_bill - group_bill;
if a and b;
run;
pandas предоставляет Преобразование механизм, позволяющий выражать такие операции кратко в одной операции.
In [1]: gb = tips.groupby("smoker")["total_bill"]
In [2]: tips["adj_total_bill"] = tips["total_bill"] - gb.transform("mean")
In [3]: tips
Out[3]:
total_bill tip sex smoker day time size adj_total_bill
67 1.07 1.00 Female Yes Sat Dinner 1 -17.686344
92 3.75 1.00 Female Yes Fri Dinner 2 -15.006344
111 5.25 1.00 Female No Sat Dinner 1 -11.938278
145 6.35 1.50 Female No Thur Lunch 2 -10.838278
135 6.51 1.25 Female No Thur Lunch 2 -10.678278
.. ... ... ... ... ... ... ... ...
182 43.35 3.50 Male Yes Sun Dinner 3 24.593656
156 46.17 5.00 Male No Sun Dinner 6 28.981722
59 46.27 6.73 Male No Sat Dinner 4 29.081722
212 46.33 9.00 Male No Sat Dinner 4 29.141722
170 48.81 10.00 Male Yes Sat Dinner 3 30.053656
[244 rows x 8 columns]
Обработка по группам#
В дополнение к агрегации, pandas groupby может использоваться для
воспроизведения большинства других групповых обработок из SAS. Например,
это DATA шаг читает данные по группам пол/курильщик и фильтрует
первую запись для каждой.
proc sort data=tips;
by sex smoker;
run;
data tips_first;
set tips;
by sex smoker;
if FIRST.sex or FIRST.smoker then output;
run;
В pandas это будет записано как:
In [4]: tips.groupby(["sex", "smoker"]).first()
Out[4]:
total_bill tip day time size adj_total_bill
sex smoker
Female No 5.25 1.00 Sat Dinner 1 -11.938278
Yes 1.07 1.00 Sat Dinner 1 -17.686344
Male No 5.51 2.00 Thur Lunch 2 -11.678278
Yes 5.25 5.15 Sun Dinner 2 -13.506344
Другие соображения#
Диск vs память#
pandas работает исключительно в памяти, тогда как набор данных SAS существует на диске. Это означает, что объем данных, который можно загрузить в pandas, ограничен памятью вашего компьютера, но также и то, что операции с этими данными могут выполняться быстрее.
Если требуется обработка данных вне оперативной памяти, одним из вариантов является
dask.dataframe
библиотека (в настоящее время в разработке), которая
предоставляет подмножество функциональности pandas для работы с данными на диске DataFrame
Взаимодействие данных#
pandas предоставляет read_sas() метод, который может читать данные SAS, сохранённые
в двоичном формате XPORT или SAS7BDAT.
libname xportout xport 'transport-file.xpt';
data xportout.tips;
set tips(rename=(total_bill=tbill));
* xport variable names limited to 6 characters;
run;
df = pd.read_sas("transport-file.xpt")
df = pd.read_sas("binary-file.sas7bdat")
Вы также можете указать формат файла напрямую. По умолчанию pandas попытается определить формат файла на основе его расширения.
df = pd.read_sas("transport-file.xpt", format="xport")
df = pd.read_sas("binary-file.sas7bdat", format="sas7bdat")
XPORT — относительно ограниченный формат, и его парсинг не так оптимизирован, как у некоторых других ридеров pandas. Альтернативный способ обмена данными между SAS и pandas — сериализация в csv.
# version 0.17, 10M rows
In [8]: %time df = pd.read_sas('big.xpt')
Wall time: 14.6 s
In [9]: %time df = pd.read_csv('big.csv')
Wall time: 4.86 s