|
|
Stata/Python 集成第 8 部分:使用 Stata Function Interface 将数据从 Stata 复制到 Python
引言
在之前的文章中,我使用了 read_stata() 方法将 Stata 数据集读入 pandas 数据框。当您想将整个 Stata 数据集读入 Python 时,这非常有效。但有时我们希望将 Stata 数据集中的部分变量或观察值,或两者,读入 Python。在这篇文章中,我将向您介绍 Stata Function Interface (SFI) 模块,并向您展示如何使用它将部分数据集读入 pandas 数据框。
如果您不熟悉 Python,在继续阅读之前,阅读我的 Stata/Python 集成系列的前四篇文章可能会有所帮助:
使用 SFI 模块将数据从 Stata 移动到 Python
SFI 是一个 Python 模块,允许您在 Stata 和 Python 之间来回传递信息。您可以复制整个或部分数据集、数据框、局部和全局宏、标量和矩阵,甚至全局 Mata 矩阵。功能太多,无法在一篇博客文章中全部展示。因此,今天我将向您展示一个您可能会使用的功能:将部分 Stata 数据集读入 Python。我们将在未来的文章中探索更多 SFI 功能。
让我们首先在下面的代码块中使用 auto 数据集。接下来,让我们进入 Python 环境并从 SFI 模块导入 Data 类。然后,我们将使用 Data 类中的 get() 方法将变量 foreign 复制到一个名为 dataraw 的 Python 列表对象中。get() 方法的第一个参数是放在单引号中的 Stata 变量列表。
sysuse auto
python
from sfi import Data
dataraw = Data.get('foreign')
dataraw
end
Python 输出显示列表对象 dataraw 包含 Stata 变量 foreign 的数据。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> dataraw = Data.get('foreign')
>>> dataraw
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
> , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
> , 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
>>> end
--------------------------------------------------------------------------------
指定观察值范围
get() 方法的第二个参数允许我们指定观察值范围。我在下面的代码块中使用了 range() 函数来指定观察值 46 到 56。注意,我还将 mpg 和 rep78 添加到了变量列表中。
python
from sfi import Data
dataraw = Data.get('foreign mpg rep78',
range(46,56))
dataraw
end
Python 输出显示列表对象 dataraw 的内容。该列表包含子列表,每个子列表包括三个值。每个子列表是 Stata 数据集中的一个观察值,包含变量 foreign、mpg 和 rep78 的数据。第五个观察值中的数字 8.98846567431158e+307 是一个缺失值,我们将在下面学习如何处理它。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56))
>>> dataraw
[[0, 18, 4], [0, 18, 1], [0, 19, 3], [0, 19, 3], [0, 19, 8.98846567431158e+307],
> [0, 24, 2], [1, 17, 5], [1, 23, 3], [1, 25, 4], [1, 23, 4]]
>>> end
--------------------------------------------------------------------------------
使用指示变量指定观察值
get() 方法的第三个参数允许我们根据指示变量进一步限制我们的数据。在下面的示例中,我生成了一个名为 touse 的新变量,如果 mpg 小于 20,则等于 1,否则等于 0。然后,我在 get() 中指定了 "touse" 作为第三个参数。
generate touse = mpg<20
python
from sfi import Data
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse")
dataraw
end
Python 输出显示 dataraw 仅包含 mpg 小于 20 的观察值。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse")
>>> dataraw
[[0, 18, 4], [0, 18, 1], [0, 19, 3], [0, 19, 3], [0, 19, 8.98846567431158e+307],
> [1, 17, 5]]
>>> end
--------------------------------------------------------------------------------
获取值标签而不是数字
Stata 变量 foreign 的值被标记为 "Domestic" 对应 0,"Foreign" 对应 1。
. list foreign in 50/54
+----------+
| foreign |
|----------|
50. | Domestic |
51. | Domestic |
52. | Domestic |
53. | Foreign |
54. | Foreign |
+----------+
我们的 Python 列表对象 dataraw 仅存储底层数字值 0 和 1,但我们可能更希望使用标签。get() 的第四个参数允许我们将 Stata 变量的值标签而不是数字传递给 Python。我在下面的代码块中指定了 valuelabel=True,以将值标签传递给 Python。
python
from sfi import Data
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True)
dataraw
end
下面的 Python 输出显示 dataraw 现在包含单词 "Domestic" 和 "Foreign"。请注意,这些是字符串而不是带标签的数字值。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,)
>>> dataraw
[['Domestic', 18, 4], ['Domestic', 18, 1], ['Domestic', 19, 3], ['Domestic', 19,
> 3], ['Domestic', 19, 8.98846567431158e+307], ['Foreign', 17, 5]]
>>> end
--------------------------------------------------------------------------------
为缺失值指定数字
get() 的第五个参数允许我们为缺失数据指定一个值。回想一下,Stata 将缺失值存储为数值存储类型的最大可能值。Stata 变量 rep78 存储为双精度数值变量,最大值为 8.98846567431158e+307。浮点数值变量的最大值为 1.70141173319e+38,long 变量的最大值为 2,147,483,620,int 变量的最大值为 32,740,byte 变量的最大值为 100。因此,缺失值的确切值取决于变量的存储类型。
Python 不将这些数字识别为缺失值。Python 将 8.98846567431158e+307 解释为一个数字。Python 中的缺失数值通常用 Numpy 的特殊浮点值 "nan" 表示,该值最早由电气和电子工程师协会在 IEEE 754-1985 标准中定义。我们可以通过为 get() 的第五个参数指定 missingval=np.nan,告诉 Python 8.98846567431158e+307 是"不是数字"(nan)。
python
from sfi import Data
import numpy as np
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True,
missingval=np.nan)
dataraw
end
下面的 Python 输出显示 dataraw 中的数字 8.98846567431158e+307 已被 Numpy 的特殊浮点值 "nan" 替换。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> import numpy as np
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,
... missingval=np.nan)
>>> dataraw
[['Domestic', 18, 4], ['Domestic', 18, 1], ['Domestic', 19, 3], ['Domestic', 19,
> 3], ['Domestic', 19, nan], ['Foreign', 17, 5]]
>>> end
--------------------------------------------------------------------------------
将列表对象转换为 pandas 数据框
我们使用 get() 将 Stata 数据集的一部分复制到一个名为 dataraw 的 Python 列表对象中。接下来,让我们将我们的列表对象转换为 pandas 数据框。
我们首先使用别名 pd 导入 pandas。然后,我们可以通过输入 dataframe = pd.DataFrame(dataraw) 创建一个数据框。
python
from sfi import Data
import numpy as np
import pandas as pd
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True,
missingval=np.nan)
dataframe = pd.DataFrame(dataraw)
dataframe
end
下面的 Python 输出显示数据框 dataframe。标记为 0、1 和 2 的列分别是变量 foreign、mpg 和 rep78。左侧未标记的列是 pandas 创建的用于唯一标识每一行的索引。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> import numpy as np
>>> import pandas as pd
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,
... missingval=np.nan)
>>> dataframe = pd.DataFrame(dataraw)
>>> dataframe
0 1 2
0 Domestic 18 4
1 Domestic 18 1
2 Domestic 19 3
3 Domestic 19 3
4 Domestic 19 NaN
5 Foreign 17 5
>>> end
--------------------------------------------------------------------------------
标记数据框的列
我们可以使用 DataFrame() 方法中的 columns 选项来标记我们的数据框的列。列名列表必须用方括号括起来,每个列名必须用单引号括起来并用逗号分隔。
python
from sfi import Data
import numpy as np
import pandas as pd
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True,
missingval=np.nan)
dataframe = pd.DataFrame(dataraw,
columns=['foreign', 'mpg', 'rep78'])
dataframe
end
下面的 Python 输出显示数据框中的第二、第三和第四列现在分别命名为 foreign、mpg 和 rep78。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> import numpy as np
>>> import pandas as pd
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,
... missingval=np.nan)
>>> dataframe = pd.DataFrame(dataraw,
... columns=['foreign', 'mpg', 'rep78'])
>>> dataframe
foreign mpg rep78
0 Domestic 18 4
1 Domestic 18 1
2 Domestic 19 3
3 Domestic 19 3
4 Domestic 19 NaN
5 Foreign 17 5
>>> end
--------------------------------------------------------------------------------
使数据框索引从 1 开始
Python 使用从零开始的数组索引,这意味着行和列计数从 0 而不是 1 开始。因此 pandas 自动创建了一个从 0 开始的行索引。如果您对索引从零开始感到满意,或者您不打算使用索引,可以跳到下一节。或者,您可以使用 DataFrame() 方法中的 index 选项将索引更改为从 1 开始。
我们将使用 Numpy 模块中的 arange() 方法来指定索引。第一个参数是行索引的第一个元素,即 1。第二个参数是行索引的最后一个元素。我们可以简单地输入 6,因为我们的数据框中有 6 行。但是,下次我们运行代码时,这个数字可能会改变。我们可以使用 len() 方法来计算列表对象 dataraw 的长度。并且我们必须在 dataraw 的长度上加 1,因为 Python 从 0 开始计数。
python
from sfi import Data
import numpy as np
import pandas as pd
dataraw = Data.get('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True,
missingval=np.nan)
dataframe = pd.DataFrame(dataraw,
columns=['foreign', 'mpg', 'rep78'],
index=[np.arange(1, len(dataraw)+1)])
dataframe
end
下面的 Python 输出显示 dataframe 的索引现在从 1 开始,以 6 结束。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> import pandas as pd
>>> import numpy as np
>>> dataraw = Data.get('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,
... missingval=np.nan)
>>> dataframe = pd.DataFrame(dataraw,
... columns=['foreign', 'mpg', 'rep78'],
... index=[np.arange(1, len(dataraw)+1)])
>>> dataframe
foreign mpg rep78
1 Domestic 18 4
2 Domestic 18 1
3 Domestic 19 3
4 Domestic 19 3
5 Domestic 19 NaN
6 Foreign 17 5
>>> end
--------------------------------------------------------------------------------
使用 getAsDict()
您还可以使用 getAsDict() 将 Stata 数据复制到 Python 字典中。参数与 get() 相同,生成的字典包含 Stata 变量的名称。这意味着我们在将字典转换为数据框时不必命名列。创建从 1 开始的数据框索引是不同的,因为字典的长度不是 Stata 观察值的数量。在下面的代码块中,我将 obs 定义为字典 dataraw 中值列表的长度。我使用 next() 和 iter() 函数来循环遍历字典 dataraw 中的值。并且我再次加了 1,因为 Python 从 0 开始计数。
python
from sfi import Data
import pandas as pd
import numpy as np
dataraw = Data.getAsDict('foreign mpg rep78',
range(46,56),
"touse",
valuelabel=True,
missingval=np.nan)
dataraw
obs = len(next(iter(dataraw.values()))) + 1
dataframe = pd.DataFrame(dataraw,
index=[np.arange(1, obs)])
dataframe
end
下面的 Python 输出显示生成的数据框看起来非常像我们使用 get() 创建的数据框。
. python
----------------------------------------------- python (type end to exit) ------
>>> from sfi import Data
>>> import pandas as pd
>>> import numpy as np
>>> dataraw = Data.getAsDict('foreign mpg rep78',
... range(46,56),
... "touse",
... valuelabel=True,
... missingval=np.nan)
>>> dataraw
{'foreign': ['Domestic', 'Domestic', 'Domestic', 'Domestic', 'Domestic', 'Foreig
> n'], 'mpg': [18, 18, 19, 19, 19, 17], 'rep78': [4, 1, 3, 3, nan, 5]}
>>> obs = len(next(iter(dataraw.values()))) + 1
>>> dataframe = pd.DataFrame(dataraw,
... index=[np.arange(1, obs)])
>>> dataframe
foreign mpg rep78
1 Domestic 18 4.0
2 Domestic 18 1.0
3 Domestic 19 3.0
4 Domestic 19 3.0
5 Domestic 19 NaN
6 Foreign 17 5.0
>>> end
--------------------------------------------------------------------------------
基础知识
也许您不希望限制您的样本,并且您不介意从零开始的索引。您只是想将一组变量复制到 Python 中的 pandas 数据框。下面的代码块将完成此操作并将 Stata 缺失值转换为 Python 缺失值。
python
from sfi import Data
import pandas as pd
import numpy as np
dataraw = Data.getAsDict('foreign mpg rep78',
None,
None,
valuelabel=False,
missingval=np.nan)
dataframe = pd.DataFrame(dataraw)
dataframe
end
下面的输出显示数据框 dataframe,它已准备好用于绘图或数据分析。
>>> dataframe
foreign mpg rep78
0 0 22 3.0
1 0 17 3.0
2 0 22 NaN
3 0 20 3.0
4 0 15 4.0
.. ... ... ...
69 1 23 4.0
70 1 41 5.0
71 1 25 4.0
72 1 25 4.0
73 1 17 5.0
[74 rows x 3 columns]
结论
我们做到了!我们使用 SFI 模块中 Data 类的 get() 和 getAsDict() 方法将 Stata 数据集的一部分复制到 Python 数据框。我们甚至处理了缺失数据。在我们的 do-files、ado-files 和 Python 脚本中,任何时候我们想将 Python 纳入我们的数据管理、分析或报告中,都很容易使用 get() 和 getAsDict()。下次,我将向您展示如何使用 SFI 将数据从 Python 复制到 Stata 数据集。
进一步阅读
Di Russo, J. 2019. Navigating The Hell of NaNs in Python, Towards Data Science (blog), October 23, 2019.
Fung, K. 2020. Array Indexing: 0-based or 1-based?, Analytics Vidhya, January 26, 2020.
在线留言
尊敬的客户朋友,如您有任何意见建议,请通过下表反馈给我们,我们会尽快与您联系。
|
|
|