查看原文
其他

Python与Stata, R, SAS, SQL在数据处理上的比较, 含code及细致讲解

Py计量研究小组 计量经济圈 2021-10-23

凡是搞计量经济的,都关注这个号了

箱:econometrics666@sina.cn

所有计量经济圈方法论丛的code程序, 宏微观数据库和各种软件都放在社群里.欢迎到计量经济圈社群交流访问.

前些日,咱们圈子引荐了①“实证研究中用到的200篇文章, 社科学者常备toolkit”、②实证文章写作常用到的50篇名家经验帖, 学者必读系列、③过去10年AER上关于中国主题的Articles专辑、④AEA公布2017-19年度最受关注的十大研究话题, 给你的选题方向,受到各位学者欢迎和热议,很多博士生导师纷纷推荐给指导的学生参阅。


之前,咱们小组引荐了1.Python中的计量回归模块及所有模块概览2.空间计量软件代码资源集锦(Matlab/R/Python/SAS/Stata), 不再因空间效应而感到孤独3.回归、分类与聚类:三大方向剖解机器学习算法的优缺点(附Python和R实现)4.机器学习第一书, 数据挖掘, 推理和预测5.从线性回归到机器学习, 一张图帮你文献综述6.11种与机器学习相关的多元变量分析方法汇总7.机器学习和大数据计量经济学, 你必须阅读一下这篇8.机器学习与Econometrics的书籍推荐, 值得拥有的经典9.机器学习在微观计量的应用最新趋势: 大数据和因果推断10.机器学习在微观计量的应用最新趋势: 回归模型11.机器学习对计量经济学的影响, AEA年会独家报道12.机器学习,可异于数理统计等,受到很多年轻学者的推崇和积极评价。


上一日,咱们引荐了“因果推断研究小组惊动了阿里巴巴!”,受到了很多业界和学界人士的热烈欢迎。今天,咱们引荐“Python做因果推断的方法示例, 解读与code”,让各位学者了解一下Python在因果推断中的使用情况。Python与Matlab、R、Stata等软件某种程度上相似,通过pip install安装packages,然后import这个package里的程序进行模型参数估计;而且,很多package开发者都会附上docs,里面有或简或繁的操作示例。如此看来,只是换了一套工具做模型估计,而计量思想都是一致的,因此这对经常关注圈子和社群的学者不会太难。

在引荐了Python做因果推断的方法示例, 解读与code后,咱们引荐一下“Python(主要是Pandas)与Stata, R, SAS, SQL在数据处理上的比较”。Python的网络课程是最多的(多到不能再多),而且大多是优质的免费课程,建议各位学者随便在视频网站搜搜。对于Python,R等课程,各位学者一定要记住:“免费的一定是最好的”,因为他们在全球开源。

1.与Stata的比较

对于来自 Stata 的潜在用户,本节旨在演示如何在 pandas 中做各种类似Stata的操作。

如果您是 pandas 的新手,您可能需要先阅读十分钟入门Pandas 以熟悉本库。

按照惯例,我们按如下方式导入 pandas 和 NumPy:

In [1]: import pandas as pd

In [2]: import numpy as np

注意

在本教程中,DataFrame将通过调用显示 pandas df.head(),它将显示该行的前N行(默认为5行)DataFrame。这通常用于交互式工作(例如Jupyter笔记本或终端) - Stata中的等价物将是:

list in 1/5

数据结构

一般术语对照表

PandasStata
DataFrame数据集(data set)
column变量(variable)
row观察(observation)
groupbybysort
NaN.

DataFrameSeries

pandas 中的 DataFrame 类似于 Stata 数据集-具有不同类型的标记列的二维数据源。如本文档所示,几乎任何可以应用于Stata中的数据集的操作也可以在 pandas 中完成。

Series 是表示DataFrame的一列的数据结构。Stata 对于单个列没有单独的数据结构,但是通常,使用 Series 类似于引用Stata中的数据集的列。

Index

每个 DataFrame 和 Series 在数据  上都有一个叫 Index-label 的标签。在 Stata 中没有相似的概念。在Stata中,数据集的行基本上是无标签的,除了可以用 _n 访问的隐式整数索引。

在pandas中,如果未指定索引,则默认情况下也使用整数索引(第一行= 0,第二行= 1,依此类推)。虽然使用标记Index或 MultiIndex可以启用复杂的分析,并且最终是 pandas 理解的重要部分,但是对于这种比较,我们基本上会忽略它, Index并且只是将其DataFrame视为列的集合。有关如何有效使用的更多信息, 请参阅索引文档Index

数据输入/输出

从价值观构建数据帧

通过将数据放在input语句之后并指定列名,可以从指定值构建Stata数据集。

input x y
1 2
3 4
5 6
end

pandas 的 DataFrame 可以用许多不同的方式构建,但对于少量的值,通常可以方便地将其指定为Python字典,其中键是列名,值是数据。

In [3]: df = pd.DataFrame({'x': [1, 3, 5], 'y': [2, 4, 6]})

In [4]: df
Out[4]:
x y
0 1 2
1 3 4
2 5 6

读取外部数据

与Stata一样,pandas提供了从多种格式读取数据的实用程序。tips在pandas测试(csv)中找到的数据集将用于以下许多示例中。

Stata提供将csv数据读入内存中的数据集。如果文件在当前工作目录中,我们可以按如下方式导入它。import delimited````tips.csv

import delimited tips.csv

pandas 方法是read_csv()类似的。此外,如果提供了网址,它将自动下载数据集。

In [5]: url = ('https://raw.github.com/pandas-dev'
...: '/pandas/master/pandas/tests/data/tips.csv')
...:

In [6]: tips = pd.read_csv(url)

In [7]: tips.head()
Out[7]:
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

比如,可以使用许多参数来指定数据应该如何解析。例如,如果数据是由制表符分隔的,没有列名,并且存在于当前工作目录中,则pandas命令将为:import delimitedread_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)

pandas 还可以用于 .dta 的文件格式中。使用read_stata()函数读取格式的Stata数据集。

df = pd.read_stata('data.dta')

除了text / csv和Stata文件之外,pandas还支持各种其他数据格式,如Excel,SAS,HDF5,Parquet和SQL数据库。这些都是通过pd.read_* 函数读取的。有关更多详细信息,请参阅IO文档

导出数据

stata 中 import delimated 的反向操作是 export delimated

export delimited tips2.csv

类似地,在 pandas 中,read_csv 的反向操作是DataFrame.to_csv()

tips.to_csv('tips2.csv')

pandas 还可以使用DataFrame.to_stata()方法导出为Stata文件格式。

tips.to_stata('tips2.dta')

数据操作

列上的操作

在Stata中,任意数学表达式可以与新列或现有列上的generate和 replace命令一起使用。该drop命令从数据集中删除列。

replace total_bill = total_bill - 2
generate new_bill = total_bill / 2
drop new_bill

pandas 通过指定个体提供了类似的矢量化操作SeriesDataFrame。可以以相同的方式分配新列。该DataFrame.drop()方法从中删除一列DataFrame

In [8]: tips['total_bill'] = tips['total_bill'] - 2

In [9]: tips['new_bill'] = tips['total_bill'] / 2

In [10]: tips.head()
Out[10]:
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

In [11]: tips = tips.drop('new_bill', axis=1)

过滤

在Stata中过滤是通过 if 一个或多个列上的子句完成的。

list if total_bill > 10

DataFrame可以通过多种方式进行过滤; 最直观的是使用 布尔索引

In [12]: tips[tips['total_bill'] > 10].head()
Out[12]:
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

如果/那么逻辑

在Stata中,if子句也可用于创建新列。

generate bucket = "low" if total_bill < 10
replace bucket = "high" if total_bill >= 10

使用 numpy 的 where 方法可以在 pandas 中完成相同的操作。

In [13]: tips['bucket'] = np.where(tips['total_bill'] < 10, 'low', 'high')

In [14]: tips.head()
Out[14]:
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

日期功能

Stata提供了各种函数来对date / datetime列进行操作。

generate date1 = mdy(1, 15, 2013)
generate date2 = date("Feb152015", "MDY")

generate date1_year = year(date1)
generate date2_month = month(date2)

* shift date to beginning of next month
generate date1_next = mdy(month(date1) + 1, 1, year(date1)) if month(date1) != 12
replace date1_next = mdy(1, 1, year(date1) + 1) if month(date1) == 12
generate months_between = mofd(date2) - mofd(date1)

list date1 date2 date1_year date2_month date1_next months_between

等效的 pandas 操作如下所示。除了这些功能外,pandas 还支持 Stata 中不具备的其他时间序列功能(例如时区处理和自定义偏移) - 有关详细信息,请参阅时间序列文档

In [15]: tips['date1'] = pd.Timestamp('2013-01-15')

In [16]: tips['date2'] = pd.Timestamp('2015-02-15')

In [17]: tips['date1_year'] = tips['date1'].dt.year

In [18]: tips['date2_month'] = tips['date2'].dt.month

In [19]: tips['date1_next'] = tips['date1'] + pd.offsets.MonthBegin()

In [20]: tips['months_between'] = (tips['date2'].dt.to_period('M')
....: - tips['date1'].dt.to_period('M'))
....:

In [21]: tips[['date1', 'date2', 'date1_year', 'date2_month', 'date1_next',
....: 'months_between']].head()
....:
Out[21]:
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>

列的选择

Stata 提供了选择,删除和重命名列的关键字。

keep sex total_bill tip

drop sex

rename total_bill total_bill_2

下面的 pandas 表示相同的操作。请注意,与 Stata 相比,这些操作不会发生。要使这些更改保持不变,请将操作分配回变量。

# keep
In [22]: tips[['sex', 'total_bill', 'tip']].head()
Out[22]:
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

# drop
In [23]: tips.drop('sex', axis=1).head()
Out[23]:
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

# rename
In [24]: tips.rename(columns={'total_bill': 'total_bill_2'}).head()
Out[24]:
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

按值排序

Stata中的排序是通过 sort

sort sex total_bill

pandas 对象有一个DataFrame.sort_values()方法,它采用列表进行排序。

In [25]: tips = tips.sort_values(['sex', 'total_bill'])

In [26]: tips.head()
Out[26]:
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

字符串处理

查找字符串的长度

Stata 分别使用ASCII和Unicode字符串 strlen() 和 ustrlen() 函数确定字符串的长度。

generate strlen_time = strlen(time)
generate ustrlen_time = ustrlen(time)

Python 使用该 len 函数确定字符串的长度。在Python 3中,所有字符串都是Unicode字符串。len包括尾随空白。使用lenrstrip排除尾随空格。

In [27]: tips['time'].str.len().head()
Out[27]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64

In [28]: tips['time'].str.rstrip().str.len().head()
Out[28]:
67 6
92 6
111 6
145 5
135 5
Name: time, dtype: int64

找到字符串的位置

Stata使用该strpos()函数确定字符串中字符的位置。这将获取第一个参数定义的字符串,并搜索您提供的子字符串的第一个位置作为第二个参数。

generate str_position = strpos(sex, "ale")

Python使用find()函数确定字符串中字符的位置。find搜索子字符串的第一个位置。如果找到子字符串,则该函数返回其位置。请记住,Python索引是从零开始的,如果找不到子串,函数将返回-1。

In [29]: tips['sex'].str.find("ale").head()
Out[29]:
67 3
92 3
111 3
145 3
135 3
Name: sex, dtype: int64

按位置提取字符串

Stata根据substr()函数的位置从字符串中提取字符串。

generate short_sex = substr(sex, 1, 1)

使用pandas,您可以使用[]符号从位置位置提取字符串中的子字符串。请记住,Python索引是从零开始的。

In [30]: tips['sex'].str[0:1].head()
Out[30]:
67 F
92 F
111 F
145 F
135 F
Name: sex, dtype: object

提取第n个字符

Stata word()函数返回字符串中的第n个单词。第一个参数是要解析的字符串,第二个参数指定要提取的字。

clear
input str20 string
"John Smith"
"Jane Cook"
end

generate first_name = word(name, 1)
generate last_name = word(name, -1)

Python使用正则表达式根据文本从字符串中提取字符串。有更强大的方法,但这只是一个简单的方法。

In [31]: firstlast = pd.DataFrame({'string': ['John Smith', 'Jane Cook']})

In [32]: firstlast['First_Name'] = firstlast['string'].str.split(" ", expand=True)[0]

In [33]: firstlast['Last_Name'] = firstlast['string'].str.rsplit(" ", expand=True)[0]

In [34]: firstlast
Out[34]:
string First_Name Last_Name
0 John Smith John John
1 Jane Cook Jane Jane

改变案例

所述的Stata strupper()strlower()strproper(), ustrupper()ustrlower(),和ustrtitle()功能分别改变ASCII和Unicode字符串的情况下,。

clear
input str20 string
"John Smith"
"Jane Cook"
end

generate upper = strupper(string)
generate lower = strlower(string)
generate title = strproper(string)
list

等效Python的功能upperlowertitle

In [35]: firstlast = pd.DataFrame({'string': ['John Smith', 'Jane Cook']})

In [36]: firstlast['upper'] = firstlast['string'].str.upper()

In [37]: firstlast['lower'] = firstlast['string'].str.lower()

In [38]: firstlast['title'] = firstlast['string'].str.title()

In [39]: firstlast
Out[39]:
string upper lower title
0 John Smith JOHN SMITH john smith John Smith
1 Jane Cook JANE COOK jane cook Jane Cook

合并

合并示例中将使用以下表格

In [40]: df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
....: 'value': np.random.randn(4)})
....:

In [41]: df1
Out[41]:
key value
0 A 0.469112
1 B -0.282863
2 C -1.509059
3 D -1.135632

In [42]: df2 = pd.DataFrame({'key': ['B', 'D', 'D', 'E'],
....: 'value': np.random.randn(4)})
....:

In [43]: df2
Out[43]:
key value
0 B 1.212112
1 D -0.173215
2 D 0.119209
3 E -1.044236

在Stata中,要执行合并,一个数据集必须在内存中,另一个必须作为磁盘上的文件名引用。相比之下,Python必须DataFrames已经在内存中。

默认情况下,Stata执行外部联接,其中两个数据集的所有观察值在合并后都保留在内存中。通过使用在_merge变量中创建的值,可以仅保留来自初始数据集,合并数据集或两者的交集的观察 。

* First create df2 and save to disk
clear
input str1 key
B
D
D
E
end
generate value = rnormal()
save df2.dta

* Now create df1 in memory
clear
input str1 key
A
B
C
D
end
generate value = rnormal()

preserve

* Left join
merge 1:n key using df2.dta
keep if _merge == 1

* Right join
restore, preserve
merge 1:n key using df2.dta
keep if _merge == 2

* Inner join
restore, preserve
merge 1:n key using df2.dta
keep if _merge == 3

* Outer join
restore
merge 1:n key using df2.dta

pandas 的 DataFrames 有一个DataFrame.merge()提供类似功能的方法。请注意,通过how关键字可以实现不同的连接类型。

In [44]: inner_join = df1.merge(df2, on=['key'], how='inner')

In [45]: inner_join
Out[45]:
key value_x value_y
0 B -0.282863 1.212112
1 D -1.135632 -0.173215
2 D -1.135632 0.119209

In [46]: left_join = df1.merge(df2, on=['key'], how='left')

In [47]: left_join
Out[47]:
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 [48]: right_join = df1.merge(df2, on=['key'], how='right')

In [49]: right_join
Out[49]:
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 [50]: outer_join = df1.merge(df2, on=['key'], how='outer')

In [51]: outer_join
Out[51]:
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

缺少数据

像Stata一样,pandas 有缺失数据的表示 - 特殊浮点值NaN(不是数字)。许多语义都是一样的; 例如,丢失的数据通过数字操作传播,默认情况下会被聚合忽略。

In [52]: outer_join
Out[52]:
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 [53]: outer_join['value_x'] + outer_join['value_y']
Out[53]:
0 NaN
1 0.929249
2 NaN
3 -1.308847
4 -1.016424
5 NaN
dtype: float64

In [54]: outer_join['value_x'].sum()
Out[54]: -3.5940742896293765

一个区别是丢失的数据无法与其哨兵值进行比较。例如,在 Stata 中,您可以执行此操作以过滤缺失值。

* Keep missing values
list if value_x == .
* Keep non-missing values
list if value_x != .

这在 pandas 中不起作用。相反,应使用pd.isna()pd.notna()函数进行比较。

In [55]: outer_join[pd.isna(outer_join['value_x'])]
Out[55]:
key value_x value_y
5 E NaN -1.044236

In [56]: outer_join[pd.notna(outer_join['value_x'])]
Out[56]:
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 还提供了多种处理丢失数据的方法,其中一些方法在Stata中表达起来很有挑战性。例如,有一些方法可以删除具有任何缺失值的所有行,用指定值(如平均值)替换缺失值,或从前一行向前填充。有关详细信息,请参阅缺失数据文档

# Drop rows with any missing value
In [57]: outer_join.dropna()
Out[57]:
key value_x value_y
1 B -0.282863 1.212112
3 D -1.135632 -0.173215
4 D -1.135632 0.119209

# Fill forwards
In [58]: outer_join.fillna(method='ffill')
Out[58]:
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

# Impute missing values with the mean
In [59]: outer_join['value_x'].fillna(outer_join['value_x'].mean())
Out[59]:
0 0.469112
1 -0.282863
2 -1.509059
3 -1.135632
4 -1.135632
5 -0.718815
Name: value_x, dtype: float64

的GroupBy

聚合

Stata collapse可用于按一个或多个关键变量进行分组,并计算数字列上的聚合。

collapse (sum) total_bill tip, by(sex smoker)

pandas提供了一种groupby允许类似聚合的灵活机制。有关 更多详细信息和示例,请参阅groupby文档

In [60]: tips_summed = tips.groupby(['sex', 'smoker'])['total_bill', 'tip'].sum()

In [61]: tips_summed.head()
Out[61]:
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

转换

在Stata中,如果组聚合需要与原始数据集一起使用bysort,通常会使用egen()。例如,减去吸烟者组每次观察的平均值。

bysort sex smoker: egen group_bill = mean(total_bill)
generate adj_total_bill = total_bill - group_bill

pandas groupby提供了一种transform机制,允许在一个操作中简洁地表达这些类型的操作。

In [62]: gb = tips.groupby('smoker')['total_bill']

In [63]: tips['adj_total_bill'] = tips['total_bill'] - gb.transform('mean')

In [64]: tips.head()
Out[64]:
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

按组处理

除聚合外,pandas groupby还可用于复制bysortStata中的大多数其他处理。例如,以下示例按性别/吸烟者组列出当前排序顺序中的第一个观察结果。

bysort sex smoker: list if _n == 1

在 pandas 中,这将写成:

In [65]: tips.groupby(['sex', 'smoker']).first()
Out[65]:
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 和 Stata 都只在内存中运行。这意味着能够在 pandas 中加载的数据大小受机器内存的限制。如果需要进行核心处理,则有一种可能性是dask.dataframe 库,它为磁盘上的pandas功能提供了一个子集DataFrame


2.与R/R库的比较

由于 pandas 旨在为人们提供可以替代R的大量数据操作和分析的功能,因此本章节会提供较为详细的R语言的介绍以及与相关的许多第三方库的对比说明,比如我们的 pandas 库。在与R和CRAN库的比较中,我们关注以下事项:

  • 功能/灵活性:每个工具可以/不可以做什么

  • 性能:操作速度有多快。硬性数字/基准是优选的

  • 易于使用:一种工具更容易/更难使用(您可能需要对此进行判断,并进行并排代码比较)

此页面还为这些R包的用户提供了一些翻译指南。

要将 DataFrame 对象从 pandas 转化为到 R 的数据类型,有一个选择是采用HDF5文件,请参阅外部兼容性示例。

快速参考

我们将从快速参考指南开始,将dplyr与pandas等效的一些常见R操作配对。

查询、过滤、采样

RPandas
dim(df)df.shape
head(df)df.head()
slice(df, 1:10)df.iloc[:9]
filter(df, col1 == 1, col2 == 1)df.query('col1 == 1 & col2 == 1')
df[df$col1 == 1 & df$col2 == 1,]df[(df.col1 == 1) & (df.col2 == 1)]
select(df, col1, col2)df[['col1', 'col2']]
select(df, col1:col3)df.loc[:, 'col1':'col3']
select(df, -(col1:col3))df.drop(cols_to_drop, axis=1)但是看[1]
distinct(select(df, col1))df[['col1']].drop_duplicates()
distinct(select(df, col1, col2))df[['col1', 'col2']].drop_duplicates()
sample_n(df, 10)df.sample(n=10)
sample_frac(df, 0.01)df.sample(frac=0.01)

Note

R表示列的子集 (select(df,col1:col3) 的缩写更接近 Pandas 的写法,如果您有列的列表,例如 df[cols[1:3] 或 df.drop(cols[1:3]),按列名执行此操作可能会引起混乱。

排序

RPandas
arrange(df, col1, col2)df.sort_values(['col1', 'col2'])
arrange(df, desc(col1))df.sort_values('col1', ascending=False)

变换

RPandas
select(df, col_one = col1)df.rename(columns={'col1': 'col_one'})['col_one']
rename(df, col_one = col1)df.rename(columns={'col1': 'col_one'})
mutate(df, c=a-b)df.assign(c=df.a-df.b)

分组和组合

RPandas
summary(df)df.describe()
gdf <- group_by(df, col1)gdf = df.groupby('col1')
summarise(gdf, avg=mean(col1, na.rm=TRUE))df.groupby('col1').agg({'col1': 'mean'})
summarise(gdf, total=sum(col1))df.groupby('col1').sum()

基本的R用法

用Rc方法来进行切片操作

R使您可以轻松地按名称访问列(data.frame

df <- data.frame(a=rnorm(5), b=rnorm(5), c=rnorm(5), d=rnorm(5), e=rnorm(5))
df[, c("a", "c", "e")]

或整数位置

df <- data.frame(matrix(rnorm(1000), ncol=100))
df[, c(1:10, 25:30, 40, 50:100)]

按名称选择多个pandas的列非常简单

In [1]: df = pd.DataFrame(np.random.randn(10, 3), columns=list('abc'))

In [2]: df[['a', 'c']]
Out[2]:
a c
0 0.469112 -1.509059
1 -1.135632 -0.173215
2 0.119209 -0.861849
3 -2.104569 1.071804
4 0.721555 -1.039575
5 0.271860 0.567020
6 0.276232 -0.673690
7 0.113648 0.524988
8 0.404705 -1.715002
9 -1.039268 -1.157892

In [3]: df.loc[:, ['a', 'c']]
Out[3]:
a c
0 0.469112 -1.509059
1 -1.135632 -0.173215
2 0.119209 -0.861849
3 -2.104569 1.071804
4 0.721555 -1.039575
5 0.271860 0.567020
6 0.276232 -0.673690
7 0.113648 0.524988
8 0.404705 -1.715002
9 -1.039268 -1.157892

通过整数位置选择多个不连续的列可以通过iloc索引器属性和 numpy.r_ 的组合来实现。

In [4]: named = list('abcdefg')

In [5]: n = 30

In [6]: columns = named + np.arange(len(named), n).tolist()

In [7]: df = pd.DataFrame(np.random.randn(n, n), columns=columns)

In [8]: df.iloc[:, np.r_[:10, 24:30]]
Out[8]:
a b c d e f g 7 8 9 24 25 26 27 28 29
0 -1.344312 0.844885 1.075770 -0.109050 1.643563 -1.469388 0.357021 -0.674600 -1.776904 -0.968914 -1.170299 -0.226169 0.410835 0.813850 0.132003 -0.827317
1 -0.076467 -1.187678 1.130127 -1.436737 -1.413681 1.607920 1.024180 0.569605 0.875906 -2.211372 0.959726 -1.110336 -0.619976 0.149748 -0.732339 0.687738
2 0.176444 0.403310 -0.154951 0.301624 -2.179861 -1.369849 -0.954208 1.462696 -1.743161 -0.826591 0.084844 0.432390 1.519970 -0.493662 0.600178 0.274230
3 0.132885 -0.023688 2.410179 1.450520 0.206053 -0.251905 -2.213588 1.063327 1.266143 0.299368 -2.484478 -0.281461 0.030711 0.109121 1.126203 -0.977349
4 1.474071 -0.064034 -1.282782 0.781836 -1.071357 0.441153 2.353925 0.583787 0.221471 -0.744471 -1.197071 -1.066969 -0.303421 -0.858447 0.306996 -0.028665
.. ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
25 1.492125 -0.068190 0.681456 1.221829 -0.434352 1.204815 -0.195612 1.251683 -1.040389 -0.796211 1.944517 0.042344 -0.307904 0.428572 0.880609 0.487645
26 0.725238 0.624607 -0.141185 -0.143948 -0.328162 2.095086 -0.608888 -0.926422 1.872601 -2.513465 -0.846188 1.190624 0.778507 1.008500 1.424017 0.717110
27 1.262419 1.950057 0.301038 -0.933858 0.814946 0.181439 -0.110015 -2.364638 -1.584814 0.307941 -1.341814 0.334281 -0.162227 1.007824 2.826008 1.458383
28 -1.585746 -0.899734 0.921494 -0.211762 -0.059182 0.058308 0.915377 -0.696321 0.150664 -3.060395 0.403620 -0.026602 -0.240481 0.577223 -1.088417 0.326687
29 -0.986248 0.169729 -1.158091 1.019673 0.646039 0.917399 -0.010435 0.366366 0.922729 0.869610 -1.209247 -0.671466 0.332872 -2.013086 -1.602549 0.333109

[30 rows x 16 columns]

aggregate

在R中,您可能希望将数据分成几个子集,并计算每个子集的平均值。使用名为df的data.frame并将其分成组by1by2

df <- data.frame(
v1 = c(1,3,5,7,8,3,5,NA,4,5,7,9),
v2 = c(11,33,55,77,88,33,55,NA,44,55,77,99),
by1 = c("red", "blue", 1, 2, NA, "big", 1, 2, "red", 1, NA, 12),
by2 = c("wet", "dry", 99, 95, NA, "damp", 95, 99, "red", 99, NA, NA))
aggregate(x=df[, c("v1", "v2")], by=list(mydf2$by1, mydf2$by2), FUN = mean)

groupby()方法类似于基本R的 aggregate 函数。

In [9]: df = pd.DataFrame(
...: {'v1': [1, 3, 5, 7, 8, 3, 5, np.nan, 4, 5, 7, 9],
...: 'v2': [11, 33, 55, 77, 88, 33, 55, np.nan, 44, 55, 77, 99],
...: 'by1': ["red", "blue", 1, 2, np.nan, "big", 1, 2, "red", 1, np.nan, 12],
...: 'by2': ["wet", "dry", 99, 95, np.nan, "damp", 95, 99, "red", 99, np.nan,
...: np.nan]})
...:

In [10]: g = df.groupby(['by1', 'by2'])

In [11]: g[['v1', 'v2']].mean()
Out[11]:
v1 v2
by1 by2
1 95 5.0 55.0
99 5.0 55.0
2 95 7.0 77.0
99 NaN NaN
big damp 3.0 33.0
blue dry 3.0 33.0
red red 4.0 44.0
wet 1.0 11.0

有关更多详细信息和示例,请参阅groupby文档

match%in%

在R中选择数据的常用方法是使用%in%使用该函数定义的数据match。运算符%in%用于返回指示是否存在匹配的逻辑向量:

s <- 0:4
s %in% c(2,4)

isin()方法类似于R %in%运算符:

In [12]: s = pd.Series(np.arange(5), dtype=np.float32)

In [13]: s.isin([2, 4])
Out[13]:
0 False
1 False
2 True
3 False
4 True
dtype: bool

match函数返回其第二个参数匹配位置的向量:

s <- 0:4
match(s, c(2,4))

有关更多详细信息和示例,请参阅重塑文档

tapply

tapply类似于aggregate,但数据可以是一个参差不齐的数组,因为子类大小可能是不规则的。使用调用的data.frame baseball,并根据数组检索信息team

baseball <-
data.frame(team = gl(5, 5,
labels = paste("Team", LETTERS[1:5])),
player = sample(letters, 25),
batting.average = runif(25, .200, .400))

tapply(baseball$batting.average, baseball.example$team,
max)

pandas我们可以使用pivot_table()方法来处理这个:

In [14]: import random

In [15]: import string

In [16]: baseball = pd.DataFrame(
....: {'team': ["team %d" % (x + 1) for x in range(5)] * 5,
....: 'player': random.sample(list(string.ascii_lowercase), 25),
....: 'batting avg': np.random.uniform(.200, .400, 25)})
....:

In [17]: baseball.pivot_table(values='batting avg', columns='team', aggfunc=np.max)
Out[17]:
team team 1 team 2 team 3 team 4 team 5
batting avg 0.352134 0.295327 0.397191 0.394457 0.396194

有关更多详细信息和示例,请参阅重塑文档

subset

query()方法类似于基本R subset 函数。在R中,您可能希望获取data.frame一列的值小于另一列的值的行:

df <- data.frame(a=rnorm(10), b=rnorm(10))
subset(df, a <= b)
df[df$a <= df$b,] # note the comma

pandas,有几种方法可以执行子集化。您可以使用 query()或传递表达式,就像它是索引/切片以及标准布尔索引一样:

In [18]: df = pd.DataFrame({'a': np.random.randn(10), 'b': np.random.randn(10)})

In [19]: df.query('a <= b')
Out[19]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550

In [20]: df[df.a <= df.b]
Out[20]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550

In [21]: df.loc[df.a <= df.b]
Out[21]:
a b
1 0.174950 0.552887
2 -0.023167 0.148084
3 -0.495291 -0.300218
4 -0.860736 0.197378
5 -1.134146 1.720780
7 -0.290098 0.083515
8 0.238636 0.946550

有关更多详细信息和示例,请参阅查询文档

with

使用df带有列的R中调用的data.frame的表达式a, b将使用with如下方式进行求值:

df <- data.frame(a=rnorm(10), b=rnorm(10))
with(df, a + b)
df$a + df$b # same as the previous expression

pandas等效表达式中,使用该 eval()方法将是:

In [22]: df = pd.DataFrame({'a': np.random.randn(10), 'b': np.random.randn(10)})

In [23]: df.eval('a + b')
Out[23]:
0 -0.091430
1 -2.483890
2 -0.252728
3 -0.626444
4 -0.261740
5 2.149503
6 -0.332214
7 0.799331
8 -2.377245
9 2.104677
dtype: float64

In [24]: df.a + df.b # same as the previous expression
Out[24]:
0 -0.091430
1 -2.483890
2 -0.252728
3 -0.626444
4 -0.261740
5 2.149503
6 -0.332214
7 0.799331
8 -2.377245
9 2.104677
dtype: float64

在某些情况下,eval()将比纯Python中的评估快得多。有关更多详细信息和示例,请参阅eval文档

plyr

plyr是用于数据分析的拆分应用组合策略的R库。这些函数围绕R,a for arrayslfor listsdfor中的三个数据结构data.frame。下表显示了如何在Python中映射这些数据结构。

RPython
arraylist
lists字典(dist)或对象列表(list of objects)
data.framedataframe

ddply

在R中使用名为df的data.frame的表达式,比如您有一个希望按汇总x的需求:

require(plyr)
df <- data.frame(
x = runif(120, 1, 168),
y = runif(120, 7, 334),
z = runif(120, 1.7, 20.7),
month = rep(c(5,6,7,8),30),
week = sample(1:4, 120, TRUE)
)

ddply(df, .(month, week), summarize,
mean = round(mean(x), 2),
sd = round(sd(x), 2))

pandas等效表达式中,使用该 groupby()方法将是:

In [25]: df = pd.DataFrame({'x': np.random.uniform(1., 168., 120),
....: 'y': np.random.uniform(7., 334., 120),
....: 'z': np.random.uniform(1.7, 20.7, 120),
....: 'month': [5, 6, 7, 8] * 30,
....: 'week': np.random.randint(1, 4, 120)})
....:

In [26]: grouped = df.groupby(['month', 'week'])

In [27]: grouped['x'].agg([np.mean, np.std])
Out[27]:
mean std
month week
5 1 63.653367 40.601965
2 78.126605 53.342400
3 92.091886 57.630110
6 1 81.747070 54.339218
2 70.971205 54.687287
3 100.968344 54.010081
7 1 61.576332 38.844274
2 61.733510 48.209013
3 71.688795 37.595638
8 1 62.741922 34.618153
2 91.774627 49.790202
3 73.936856 60.773900

有关更多详细信息和示例,请参阅groupby文档

重塑/ reshape2

melt.array

使用a在R中调用的3维数组的表达式,您希望将其融合到data.frame中:

a <- array(c(1:23, NA), c(2,3,4))
data.frame(melt(a))

在Python中,既然a是一个列表,你可以简单地使用列表理解。

In [28]: a = np.array(list(range(1, 24)) + [np.NAN]).reshape(2, 3, 4)

In [29]: pd.DataFrame([tuple(list(x) + [val]) for x, val in np.ndenumerate(a)])
Out[29]:
0 1 2 3
0 0 0 0 1.0
1 0 0 1 2.0
2 0 0 2 3.0
3 0 0 3 4.0
4 0 1 0 5.0
.. .. .. .. ...
19 1 1 3 20.0
20 1 2 0 21.0
21 1 2 1 22.0
22 1 2 2 23.0
23 1 2 3 NaN

[24 rows x 4 columns]

melt.list

使用aR中调用的列表的表达式,您希望将其融合到data.frame中:

a <- as.list(c(1:4, NA))
data.frame(melt(a))

在Python中,此列表将是元组列表,因此 DataFrame()方法会根据需要将其转换为数据帧。

In [30]: a = list(enumerate(list(range(1, 5)) + [np.NAN]))

In [31]: pd.DataFrame(a)
Out[31]:
0 1
0 0 1.0
1 1 2.0
2 2 3.0
3 3 4.0
4 4 NaN

有关更多详细信息和示例,请参阅“进入数据结构”文档

melt.data.frame

使用cheese在R中调用的data.frame的表达式,您要在其中重新整形data.frame:

cheese <- data.frame(
first = c('John', 'Mary'),
last = c('Doe', 'Bo'),
height = c(5.5, 6.0),
weight = c(130, 150)
)
melt(cheese, id=c("first", "last"))

在Python中,该melt()方法是R等价物:

In [32]: cheese = pd.DataFrame({'first': ['John', 'Mary'],
....: 'last': ['Doe', 'Bo'],
....: 'height': [5.5, 6.0],
....: 'weight': [130, 150]})
....:

In [33]: pd.melt(cheese, id_vars=['first', 'last'])
Out[33]:
first last variable value
0 John Doe height 5.5
1 Mary Bo height 6.0
2 John Doe weight 130.0
3 Mary Bo weight 150.0

In [34]: cheese.set_index(['first', 'last']).stack() # alternative way
Out[34]:
first last
John Doe height 5.5
weight 130.0
Mary Bo height 6.0
weight 150.0
dtype: float64

有关更多详细信息和示例,请参阅重塑文档

cast

在R中acast是一个表达式,它使用df在R中调用的data.frame 来转换为更高维的数组:

df <- data.frame(
x = runif(12, 1, 168),
y = runif(12, 7, 334),
z = runif(12, 1.7, 20.7),
month = rep(c(5,6,7),4),
week = rep(c(1,2), 6)
)

mdf <- melt(df, id=c("month", "week"))
acast(mdf, week ~ month ~ variable, mean)

在Python中,最好的方法是使用pivot_table()

In [35]: df = pd.DataFrame({'x': np.random.uniform(1., 168., 12),
....: 'y': np.random.uniform(7., 334., 12),
....: 'z': np.random.uniform(1.7, 20.7, 12),
....: 'month': [5, 6, 7] * 4,
....: 'week': [1, 2] * 6})
....:

In [36]: mdf = pd.melt(df, id_vars=['month', 'week'])

In [37]: pd.pivot_table(mdf, values='value', index=['variable', 'week'],
....: columns=['month'], aggfunc=np.mean)
....:
Out[37]:
month 5 6 7
variable week
x 1 93.888747 98.762034 55.219673
2 94.391427 38.112932 83.942781
y 1 94.306912 279.454811 227.840449
2 87.392662 193.028166 173.899260
z 1 11.016009 10.079307 16.170549
2 8.476111 17.638509 19.003494

类似地dcast,使用dfR中调用的data.frame 来基于Animal和聚合信息FeedType

df <- data.frame(
Animal = c('Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1',
'Animal2', 'Animal3'),
FeedType = c('A', 'B', 'A', 'A', 'B', 'B', 'A'),
Amount = c(10, 7, 4, 2, 5, 6, 2)
)

dcast(df, Animal ~ FeedType, sum, fill=NaN)
# Alternative method using base R
with(df, tapply(Amount, list(Animal, FeedType), sum))

Python可以通过两种不同的方式处理它。首先,类似于上面使用pivot_table()

In [38]: df = pd.DataFrame({
....: 'Animal': ['Animal1', 'Animal2', 'Animal3', 'Animal2', 'Animal1',
....: 'Animal2', 'Animal3'],
....: 'FeedType': ['A', 'B', 'A', 'A', 'B', 'B', 'A'],
....: 'Amount': [10, 7, 4, 2, 5, 6, 2],
....: })
....:

In [39]: df.pivot_table(values='Amount', index='Animal', columns='FeedType',
....: aggfunc='sum')
....:
Out[39]:
FeedType A B
Animal
Animal1 10.0 5.0
Animal2 2.0 13.0
Animal3 6.0 NaN

第二种方法是使用该groupby()方法:

In [40]: df.groupby(['Animal', 'FeedType'])['Amount'].sum()
Out[40]:
Animal FeedType
Animal1 A 10
B 5
Animal2 A 2
B 13
Animal3 A 6
Name: Amount, dtype: int64

有关更多详细信息和示例,请参阅重新整形文档或groupby文档

factor

pandas具有分类数据的数据类型。

cut(c(1,2,3,4,5,6), 3)
factor(c(1,2,3,2,2,3))

在Pandas,这是完成与pd.cutastype("category")

In [41]: pd.cut(pd.Series([1, 2, 3, 4, 5, 6]), 3)
Out[41]:
0 (0.995, 2.667]
1 (0.995, 2.667]
2 (2.667, 4.333]
3 (2.667, 4.333]
4 (4.333, 6.0]
5 (4.333, 6.0]
dtype: category
Categories (3, interval[float64]): [(0.995, 2.667] < (2.667, 4.333] < (4.333, 6.0]]

In [42]: pd.Series([1, 2, 3, 2, 2, 3]).astype("category")
Out[42]:
0 1
1 2
2 3
3 2
4 2
5 3
dtype: category
Categories (3, int64): [1, 2, 3]
有关更多详细信息和示例,请参阅分类介绍和 API文档。还有一个关于R因子差异的文档 。

以上只显示与Stata, R软件的比较,以下二维码中包括“Python与Stata, R, SAS, SQL在数据处理上的比较全文”。

长按以上二维码阅读全文

对Python做计量估计感兴趣的学者,可以到社群或相关研究小组交流讨论。

拓展性阅读

1.用R语言做Econometrics的书籍推荐, 值得拥有的经典2.18+1张图掌握R软件的方方面面, 还有谁, 还有谁?3.用R语言做空间计量, 绝不容错过的简明教程4.R软件中的时间序列分析程序包纵览5.R软件画图指南针,摆脱丑图不是梦6.平滑转移自回归模型(STAR)应用与在R软件的操作7.用R语言做空间计量, 绝不容错过的简明教程8.R语言函数最全总结, 机器学习从这里出发9.R语言ggplot2的小抄, 绘图总结查阅10.送|R语言全套视频和资料,异常珍贵的材料11.2卷RDD断点回归使用手册, 含Stata和R软件操作流程

下面这些短链接文章属于合集,可以收藏起来阅读,不然以后都找不到了。

2年,计量经济圈公众号近1000篇文章,

Econometrics Circle




数据系列:空间矩阵 | 工企数据 | PM2.5 | 市场化指数 | CO2数据 |  夜间灯光 | 官员方言  | 微观数据 |

计量系列:匹配方法 | 内生性 | 工具变量 | DID | 面板数据 | 常用TOOL | 中介调节 | 时间序列 | RDD断点 | 合成控制 | 

数据处理:Stata | R | Python | 缺失值 | CHIP/ CHNS/CHARLS/CFPS/CGSS等 |


干货系列:能源环境 | 效率研究 | 空间计量 | 国际经贸 | 计量软件 | 商科研究 | 机器学习 | SSCI | CSSCI | SSCI查询 |

计量经济圈组织了一个计量社群,有如下特征:热情互助最多、前沿趋势最多、社科资料最多、社科数据最多、科研牛人最多、海外名校最多。因此,建议积极进取和有强烈研习激情的中青年学者到社群交流探讨,始终坚信优秀是通过感染优秀而互相成就彼此的。

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存