与 SAS 的比较# 对于来自SAS 的潜在用户 ,此页面旨在演示如何在 pandas 中执行不同的 SAS 操作。 如果您是 pandas 新手,您可能需要先阅读10 分钟了解 pandas 以熟悉该库。 按照惯例,我们导入 pandas 和 NumPy,如下所示: In [1]: import pandas as pd In [2]: import numpy as np 数据结构# 通用术语翻译# 熊猫 SAS DataFrame 数据集 柱子 多变的 排 观察 通过...分组 BY组 NaN . DataFrame# pandas 中的ADataFrame类似于 SAS 数据集 - 带有可以是不同类型的标记列的二维数据源。正如本文档所示,几乎任何可以使用 SASDATA步骤应用于数据集的操作也可以在 pandas 中完成。 Series# ASeries是表示 a 的一列的数据结构 DataFrame。 SAS 没有针对单个列的单独数据结构,但一般来说,使用 aSeries类似于在DATA步骤中引用列。 Index# 每个DataFrameandSeries都有一个- ,它们是数据行Index上的标签 。 SAS 没有完全类似的概念。除了可以在步骤 ( )期间访问的隐式整数索引之外,数据集的行本质上是未标记的。DATA_N_ 在pandas中,如果没有指定索引,默认情况下也会使用整数索引(第一行= 0,第二行= 1,依此类推)。虽然使用带标签的Indexor MultiIndex可以进行复杂的分析,并且最终是 pandas 需要理解的重要部分,但对于这种比较,我们基本上会忽略 , Index而只是将 视为DataFrame列的集合。请参阅索引文档以了解有关如何 Index有效使用的更多信息。 副本与就地操作# Series大多数 pandas 操作都会返回/的副本DataFrame。要使更改“坚持”,您需要分配给一个新变量: sorted_df = df.sort_values("col1") 或者覆盖原来的: df = df.sort_values("col1") 笔记 您将看到某些方法可使用inplace=Trueor关键字参数:copy=False df.replace(5, inplace=True) inplace关于弃用和删除以及copy对于大多数方法(例如dropna),除了极小的方法子集(包括replace)之外,存在着积极的讨论。在 Copy-on-Write 上下文中,这两个关键字不再是必需的。该提案可以 在这里找到。 数据输入/输出# 从值构造一个 DataFrame # 通过将数据放置在语句后面datalines并指定列名称,可以根据指定值构建 SAS 数据集。 data df; input x y; datalines; 1 2 3 4 5 6 ; run; pandasDataFrame可以通过多种不同的方式构建,但对于少量值,将其指定为 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 提供了读取多种格式数据的实用程序。tips在 pandas 测试 ( csv ) 中找到的数据集将在以下许多示例中使用。 SAS 提供将 csv 数据读入数据集的功能。PROC IMPORT 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] 与 一样,可以采用多个参数来指定如何解析数据。例如,如果数据是制表符分隔的,并且没有列名,则 pandas 命令将为:PROC IMPORTread_csv 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) 除了text/csv之外,pandas还支持多种其他数据格式,例如Excel、HDF5和SQL数据库。这些都是通过pd.read_* 函数读取的。有关更多详细信息,请参阅IO 文档。 限制输出# 默认情况下,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; 导出数据# SAS 中的倒数为PROC IMPORTPROC 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; Seriespandas 通过指定 .pandas文件中的 个体来提供矢量化操作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 中的过滤是通过一个或多个列上的ifor语句完成的。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] 上面的语句只是将一个Seriesof 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] 如果/那么逻辑# 在 SAS 中,if/then 逻辑可用于创建新列。 data tips; set tips; format bucket $4.; if total_bill < 10 then bucket = 'low'; else bucket = 'high'; run; wherepandas 中的相同操作可以使用中的方法完成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 使用LENGTHN 和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()andSeries.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 通过...分组# 聚合# SAS可用于按一个或多个关键变量进行分组并计算数字列上的聚合。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 中,如果组聚合需要与原始帧一起使用,则必须将其重新合并在一起。例如,减go吸烟者组每次观察的平均值。 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] 按组处理# 除了聚合之外,pandasgroupby还可用于通过 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 其他注意事项# 磁盘与内存# pandas 仅在内存中运行,其中 SAS 数据集存在于磁盘上。这意味着 pandas 中能够加载的数据大小受到机器内存的限制,但对该数据的操作可能会更快。 如果需要核心处理,一种可能是 dask.dataframe 库(目前正在开发中),它为磁盘上的 pandas 功能提供了一个子集DataFrame 数据互操作# pandas提供了一种read_sas()可以读取以XPORT或SAS7BDAT二进制格式保存的SAS数据的方法。 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