R 新手学习 python pandas 中的数据结构

· 4162字 · 9分钟

此前我使用 R 处理数据,对 R base 和 data.table 包较为熟悉。现在着手学习 python,从使用 pandas 处理数据开始。下面简单地梳理一组对照关系。

|-|R|Python| |:–:|:–:| |基础数值计算|R base|内置函数、numpy| |数据处理|data.table|pandas|

R base 中的向量与 pandas 中的一维数组 🔗

以下是 R base 中创建向量、查看元素、用分位数函数计算的代码与输出结果。

```r
series <- c(1:24) # 创建一个向量
print(series) # 打印
```
[1]  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

```r
series[0] # 查看索引为0的元素
```
integer(0)

```r
series[1:3] # 查看索引为1到3的元素
```
[1] 1 2 3

```r
quantile(series[1:10], probs = c(0.25, 0.75)) # 计算前10个元素的25%和75%的分位数
```
 25%  75% 
3.25 7.75 

接下来使用 python 的 pandas 来对照实现。

```python
import pandas as pd

series = pd.Series(range(1,25)) # 创建一个一维数组
print(series) # 打印
```
0      1
1      2
...
22    23
23    24
dtype: int64

```python
series[0]
```
np.int64(1)

```python
series[1:3]
```
1    2
2    3
dtype: int64

```python
series[1:10].quantile([0.25, 0.75])
```
0.25    4.0
0.75    8.0
dtype: float64

对照来看,两者的主要区别在于使用函数的方式。R 中创建了一个向量 series,使用quantile(series)计算分位数,这种函数名(向量)用法是函数作为一个工具,将向量输入函数,然后输出函数作用后的结果。而在 Python 中创建了一个一维数组 series,作为一个数据对象,pandas 提供给 series 很多功能(或称属性),执行series.quantile()就是调用了 series 这个对象的quantile()功能,这种对象.函数名()的用法主体是对象。在使用函数的性质这点上,前者像是用工具,后者像是用功能。其他存在于两者之间的差异如下。

  1. 区间封闭差异
  • R 中区间包含末尾,如向量c(1:24)包含24。
  • Python 中区间不包含末尾,如range(1, 24)不包含24,series[1:3]会只查看索引序号为1到2的元素。
  1. 索引序号差异
  • R 中数据对象的索引序号从1开始。
  • Python 则从0开始。
  1. 符号差异
  • 表示向量
    • R 中使用c()创建向量,比如c(0.25, 0.75)
    • Python 中用[]表示向量,如[0.25, 0.75]
  • 表示区间
    • R 中使用:表示区间,比如执行1:24得到1到24个数字;也表示索引切片,如series[1:24]会查看索引为1到24的元素。
    • Python 中直接执行1:24会报错,仅仅在索引时用:表示切片,比如series[1:24]会查看索引为1到23的元素。
  • 赋值符号
    • R 中给对象赋值用<-
    • Python 中给对象赋值用=

R base 中的列表与 Python 中的列表、字典 🔗

以下是 R base 中创建一个列表,然后查看列表中的元素。R 的列表有以下几个特点。

  1. 列表的元素可以是任意类型。
  2. 列表的元素可以是不同长度。
  3. 列表中可以嵌套列表。
  4. 列表中的元素可以有名字,或者没有名字,索引序号从1开始。
  5. 分别用[]按索引序号查看子列表,用[[]]按索引序号查看元素内容,用$按名称查看子列表。
r_list <- list(c(1:24),
               col2 = LETTERS[1:6],
               col3 = list(a = 1:2, b = list(c(1, 2, 'b'))))

r_list[2]        # 按索引序号查看第2个子列表,得到一个列表
r_list[[2]]      # 按索引序号查看第2个元素,对应得到一个向量
r_list['col2']   # 按元素名称查看子列表
r_list[["col2"]] # 按元素名称查看对应元素内容
r_list$col3$b    # 按元素名称查看嵌套列表
r_list[[3]][[2]][[1]] # 按索引序号查看层层嵌套的元素内容 

以下是对照在 Python 中创建近似的列表和字典。与 R 列表相同的是:任意元素类型、元素长度不同、可以嵌套。而有区别的是以下几点。

  1. R 列表中元素可以有名或无名,python 列表中元素无名,python 字典中元素都必须有名称。
  2. R 列表支持用索引序号或名称查看元素,python 列表仅支持用索引序号,而 python 字典仅支持用名称。
  3. R 列表用list()来创建,而 python 列表用[],python 字典用{}
  4. R 列表可以查看子列表或元素内容,而 python 列表或字典均是直接查看元素内容。
  5. R 中如c(1,2,'b')同时有数值和字符串创建向量时,会把数值转换成字符串,而 python 列表或字典会保持原样。
  6. R 中分别用[][[]]$在不同情况查看元素,而 python 列表和字典统一用[]查看元素。
p_list = [range(1, 25), list("ABCDEF"),
          [
              [1, 2],
              [[1, 2, 'b']]
          ]]
p_list[1]               # 得到 ['A', 'B', 'C', 'D', 'E', 'F']
p_list[2][1][0][1]+1    # 得到 3
p_dict = {
    "col1": range(1, 25),
    "col2": list("ABCDEF"),
    "col3": {
        "a": [1, 2],
        "b": [[1, 2, 'b']]
    }
}
p_dict["col2"]            # 得到 ['A', 'B', 'C', 'D', 'E', 'F']
p_dict["col3"]["b"]       # 得到 [[1, 2, 'b']]
p_dict["col3"]["b"][0]    # 得到 [[1, 2, 'b']]
p_dict["col3"]["b"][0][1] # 得到 2

data.table 与 pandas 的数据框 🔗

以下是 R data.table 创建一个数据框。

library(data.table)

set.seed(2023)

dt <-
  data.table(
    column1 = c(1:24),
    column2 = rep(LETTERS[1:6], 4),
    column3 = sample(1:100, 24),
    column4 = sample(50:100, 24)
  )

head(dt)

以下是 Python 中创建一个同样的数据框。

import pandas as pd
import numpy as np

np.random.seed(2023)

data = {
    "column1": np.arange(1, 25),                     # 1 到 24
    "column2": np.tile(list("ABCDEF"), 4),           # 重复 A-F 共 4 次
    "column3": np.random.randint(1, 101, 24),        # 1~100 随机数
    "column4": np.random.randint(50, 101, 24)        # 50~100 随机数
}
df = pd.DataFrame(data)

df.head()

除了前几小节中的差异,两种语言在数据框之间还有更多差异,总结如下。

  1. 加载 vs 导入
  • R 加载包,该包中所有函数都会被一次性全部加载到全局空间中。
  • Python 导入包,可以导入整个包,也可以只导入包中部分函数或模块。
  1. 使用函数
  • R 中
    • 加载包以后,包中的函数都可以直接使用,比如用data.table()创建一个数据对象。
    • 嵌套使用多层函数,比如rep(LETTERS[1:6], 4)
  • Python 中
    • 使用内置函数时,用法与 R 一致,比如print(df)打印数据框。
    • 使用包中的函数时,需要加上包名作为前缀,比如用pd.DataFrame()创建一个数据对象。
    • 指定对象来使用函数的功能时,加上对象名作为前缀,如df.head()查看数据框前6行。
    • 嵌套使用多层函数时,用.的方式像链条一样层层调用指定函数,如df.head(6).min()

接下来,通过筛选行和列来比较两者差异。data.table 在筛选行列上语法极为简洁、统一,而 pandas 相比之下更加复杂,且有三种方式要进行区分,即df[...]df.iloc[...](ps指 integer location,按索引序号这样的整数代表位置),df.loc[...](ps指 location,按名称代表位置)。需要注意的是,当行的索引默认为从0开始的数字时,df.iloc[0:2]取出索引序号为切片0:2之间的行,由于python区间左闭右开的特点,实际上取出的是第1、2行;此时数据框的标签也是数字,df.loc[0:2]取出的是标签数字为0:2之间的行,那么实际取出第1、2、3行。

以下分别按序号、名称、条件筛选行或列,同时筛选行和列等方式来梳理三者之间的区别。

  • 筛选行
    • df[...]
      1. 按序号: √ 支持按行切片,比如df[0:2]。不支持df[0],这代表取出列名称为0的列。
      2. 按名称: × 不支持。
      3. 按条件: √ 支持。比如多条件df[(df["column1"] <= 3) & (df["column2"] != "B")]
    • df.iloc[...]
      1. 按序号: √ 支持。比如df.iloc[0]df.iloc[0:2]df.iloc[[0,2,4]]
      2. 按名称: × 不支持。
      3. 按条件: × 不支持。
    • df.loc[...]
      1. 按序号: × 不支持。
      2. 按名称: √ 支持。
      3. 按条件: √ 支持。比如df.loc[df["column1"] <= 3]
  • 筛选列
    • df[...]
      1. 按序号: × 不支持。
      2. 按名称: √ 支持。比如df["column1"]df[["column1","column3"]]
      3. 按条件: × 不支持。
    • df.iloc[...]
      1. 按序号: √ 支持。比如df.iloc[:, 0],df.iloc[:, [0,1,3]]`。
      2. 按名称: × 不支持。
      3. 按条件: × 不支持。
    • df.loc[...]
      1. 按序号: × 不支持。
      2. 按名称: √ 支持。比如df.loc[:, "column1"]df.loc[:, ["column1", "column2"]]
      3. 按条件: √ 支持。比如cols = df.columns[df.columns.str.contains("col")] df.loc[:, cols]
  • 同时筛选行和列
    • df[...]: × 不支持。
    • df.iloc[...]: √ 支持。比如按序号,df.iloc[0:2, 0:3]
    • df.loc[...]: √ 支持。比如按条件或名称,df.loc[df["column2"].isin(["B", "C"]), ["column2", "column3"]]

以下是练习两种语言下筛选数据框的操作。

- R data.table Python pandas
按序号筛选一行 dt[1] df.iloc[0]
按序号筛选多行 dt[7:9] df.iloc[6:9]
单条件筛选行 dt[column1 <= 3] df[df["column1"] <= 3]
多条件筛选行 dt[column1 <= 3 & !(column2 == 'B')] df[(df["column1"] <= 3) & (df["column2"] != "B")]
按序号筛选单列 dt[, 1] df.iloc[:, 0]
按序号筛选多列 dt[, c(1:2, 4)] df.iloc[:, [0,1,3]]
按序号去掉一列 dt[,-2] df.drop(df.columns[1], axis=1)
按序号去掉多列 dt[, -c(2, 4)] df.drop(df.columns[[1, 3]], axis=1)
按名称筛选列 dt[, 'column1'] df["column1"]
按名称筛选多列 dt[, c('column1','column3')] df[["column1","column3"]]
按名称去掉多列 dt[, -c('column1','column3')] df.drop(columns=["column1","column3"])
按序号筛选行和列 dt[1:2, 1:3] df.iloc[0:2, 0:3]
按条件和名称筛选行和列 dt[column2 %in% c('B', 'C'), c('column2', 'column3')] df.loc[df["column2"].isin(["B", "C"]), ["column2", "column3"]]

附录 🔗

快捷键不快捷:对比 Rstudio、Pycharm、vscode 🔗

操作系统、各类软件为了便于操作都会设置一些快捷键,但是对于本新手这是个沉重的负担。首先讲操作系统,公司里本地电脑是 UOS,服务器是 Linux,家里电脑是 Windows。然后讲各软件,操作数据库有不同的 IDE,用 R 是 Rstudio,用 Python 是 Pycharm,这次时间充裕从头学 Python 改用 vscode。最后软件和系统的快捷键偶尔还会冲突。所以整个全能 AI 助手多么好,但……

  • RStudio
    • 执行选中代码:Ctrl + Enter
    • 代码格式化:Ctrl + Shift + A
    • 批量加注释:Ctrl + Shift + C
  • Pycharm
    • 执行选中代码: shift + alt +E
    • 代码格式化:ctrl + alt +L
    • 批量加注释:ctrl + /

vscode 我现在调试 AI 写的代码创建.py文件,边学边记笔记是用其中的 jupyter notebook 插件。操作上还玩不溜……

  1. 切换终端,vscode 打开后默认终端是 bash,那么直接输入R会切换到 R 环境,而输入Python3会切换到 Python 环境。
  2. 设置 Shift + Enter 为将选中代码发送到终端执行。在vscode左下角找到“管理”(ps机械齿轮图标)单击键盘快捷方式,搜索关键词如“run selected text”找到“终端: 在活动终端运行所选文本(“command”: “workbench.action.terminal.runSelectedText”)”,然后设置好快捷键。
  3. 选中代码格式化: Ctrl + K Ctrl + F。
  4. 批量加注释:ctrl + /

好翻的文档 🔗

  1. data.table 与 pandas:https://cosx.org/2021/01/dt-pd/
  2. pandas 菜鸟教程:https://www.runoob.com/pandas/pandas-intro.html
  3. pandas 官方手册:https://pandas.pydata.org/docs/reference/index.html

最后,因为会 sql 和 R,处理数据要做的事其实差不多,换到 Python 里面大概就是函数名称变了,或者用函数的方式变了。不过搞清楚区别以后,到了做事这一步就是自然的积累过程,现在 AI 写的代码也还不错,所以只需要搞清楚换语言后的关键差异,能看懂 AI 的代码然后举一反三就可以了。记住函数名称的步骤感觉可以省略,反正可以让 AI 解释。如此大言不惭,这该不会就是俺一直是个新手的原因吧,哈哈。