pandas 入门翻译

Pandas基础教程-翻译

简单翻译,pivot 部分内容没翻译,小部分内容略有缺失。

安装

pip install pandas

1. 数据类型

想要使用,先导入包:

import pandas as pd

表格数据展示

  1. 想要展示一群人的数据,如年龄,姓名,性别,如何展示?
df = pd.DataFrame(
    {
        "Name": [
            "Braund, Mr. Owen Harris",
            "Allen, Mr. William Henry",
            "Bonnell, Miss. Elizabeth",
        ],
        "Age": [22, 35, 58],
        "Sex": ["male", "male", "female"],
    }
)


df
Out[3]: 
                       Name  Age     Sex
0   Braund, Mr. Owen Harris   22    male
1  Allen, Mr. William Henry   35    male
2  Bonnell, Miss. Elizabeth   58  female

想要存放表格类型数据,使用 DataFrame 数据类型。当使用包含列表的字典作为构造 DataFrame 的参数时,字典的键会作为每一列的表头,每个键所对应的值(列表),会作为每列的数据。

注意上面输出的结果最左边一列的0,1,2. 这是每一行的行索引标签。

一个 DataFrame 是一个二维数据结构,可以保存不同的数据类型:文本,整数,浮点数...,它有点像一个电子表格,一个 SQL 表等。

在电子表格中,上面的数据,有点像这样:

DataFrame 中的每一列数据,都是 Series 类型。

只想要 Age 列数据:

df["Age"]
Out[4]: 
0    22
1    35
2    58
Name: Age, dtype: int64

可以看出,取某一列的方式,有点像 python 字典键值对的取值方式。

你也可以自己创建一个 Series

ages = pd.Series([22, 35, 58], name="Age")

ages
Out[6]: 
0    22
1    35
2    58
Name: Age, dtype: int64

pandas Series 没有列标签,它就是一个单独的列。当然,Series有行标签。

使用 Series

  1. 想知道人群的最大年龄:

选中 Age 列,然后使用 max()

df["Age"].max()  # df 是一个DataFrame,它的 Age 列是一个 Series
Out[7]: 58

或者,直接使用Series:

ages.max()
Out[8]: 58
  1. 想知道年龄的一些基础统计分析
df.describe()
Out[9]: 
             Age
count   3.000000
mean   38.333333
std    18.230012
min    22.000000
25%    28.500000
50%    35.000000
75%    46.500000
max    58.000000

describe() 对数字类型的数据,提供了一个基础的统计总览。Name,Sex 列是文本类型的,因此不能使用此方法。

REMEMBER

导入包:import pandas as pd

表格类型数据: DataFrame

表格的每一列数据都是:Series

对 DataFrame 或 Series,可以应用一些方法

更深入数据结构:data strutures

2. 读写表格数据

titanic.csv

titanic = pd.read_csv("data/titanic.csv")

pandas 提供了 read_csv() 来读取 csv 文件到 DataFrame 中。还可以读取其他文件:(csv, excel, sql, json, parquet, …), 每个方法的前缀都是:read_*

确保每次读取完,都检查一下读取的数据。当展示 DataFrame 时,默认显示前5行和后5行:

titanic
Out[3]: 
     PassengerId  Survived  Pclass                                               Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked
0              1         0       3                            Braund, Mr. Owen Harris    male  ...      0         A/5 21171   7.2500   NaN         S
1              2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ...      0          PC 17599  71.2833   C85         C
2              3         1       3                             Heikkinen, Miss. Laina  female  ...      0  STON/O2. 3101282   7.9250   NaN         S
3              4         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  ...      0            113803  53.1000  C123         S
4              5         0       3                           Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S
..           ...       ...     ...                                                ...     ...  ...    ...               ...      ...   ...       ...
886          887         0       2                              Montvila, Rev. Juozas    male  ...      0            211536  13.0000   NaN         S
887          888         1       1                       Graham, Miss. Margaret Edith  female  ...      0            112053  30.0000   B42         S
888          889         0       3           Johnston, Miss. Catherine Helen "Carrie"  female  ...      2        W./C. 6607  23.4500   NaN         S
889          890         1       1                              Behr, Mr. Karl Howell    male  ...      0            111369  30.0000  C148         C
890          891         0       3                                Dooley, Mr. Patrick    male  ...      0            370376   7.7500   NaN         Q

[891 rows x 12 columns]

Ques: 我想展示前8行,怎么办?

titanic.head(8)
Out[4]: 
   PassengerId  Survived  Pclass                                               Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked
0            1         0       3                            Braund, Mr. Owen Harris    male  ...      0         A/5 21171   7.2500   NaN         S
1            2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ...      0          PC 17599  71.2833   C85         C
2            3         1       3                             Heikkinen, Miss. Laina  female  ...      0  STON/O2. 3101282   7.9250   NaN         S
3            4         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  ...      0            113803  53.1000  C123         S
4            5         0       3                           Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S
5            6         0       3                                   Moran, Mr. James    male  ...      0            330877   8.4583   NaN         Q
6            7         0       1                            McCarthy, Mr. Timothy J    male  ...      0             17463  51.8625   E46         S
7            8         0       3                     Palsson, Master. Gosta Leonard    male  ...      1            349909  21.0750   NaN         S

[8 rows x 12 columns]

想看前 N 行数据,使用 head(N) 方法。同样,想看后多少行,用:titanic.tail(N)

想知道各列的数据类型,用 dtypes 属性

titanic.dtypes
Out[5]: 
PassengerId      int64
Survived         int64
Pclass           int64
Name            object
Sex             object
Age            float64
SibSp            int64
Parch            int64
Ticket          object
Fare           float64
Cabin           object
Embarked        object
dtype: object

integers (int64), floats (float64), strings (object).

Ques: 如何导出成为电子表格?

titanic.to_excel("titanic.xlsx", sheet_name="passengers", index=False)

之前我们使用 read_* 方法来读取文件,现在可以使用 to_* 来导出文件。sheet_name 可以设置自定义表单名称,而不是用默认的'Sheet1', index=False 可以禁止行索引标签出现在表格中。

如果我们想读取到刚才保存的那个文件,对应的代码是:

titanic = pd.read_excel("titanic.xlsx", sheet_name="passengers")

titanic.head()
Out[8]: 
   PassengerId  Survived  Pclass                                               Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked
0            1         0       3                            Braund, Mr. Owen Harris    male  ...      0         A/5 21171   7.2500   NaN         S
1            2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ...      0          PC 17599  71.2833   C85         C
2            3         1       3                             Heikkinen, Miss. Laina  female  ...      0  STON/O2. 3101282   7.9250   NaN         S
3            4         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  ...      0            113803  53.1000  C123         S
4            5         0       3                           Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S

[5 rows x 12 columns]

Ques: 我想知道整个 DataFrame 的概括信息?

titanic.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB

info() 可以显示一个 DataFrame 的基础信息. 信息大致如下:
它是一个 DataFrame 类型。
它有 891 行,索引从 0 - 890
总共有 12 列数据
索引为 0 的 PassengerId 列,它有 891 行 非空数据,此列格式是 int64(整数型)
...
索引为 5 的 Age 列,它有 714 行非空数据,此列格式是 float64(浮点型)
...

REMEMBER

  1. 从文件读取:read_* 方法.

  2. 导出到文件:to_* 方法.

  3. head/tail/info 方法和 dtypes 属性,可以快速检查一些状态内容。

更多读写功能:more

3. DataFrame 的子集

  1. 选择某列,可以用:[列名] 的方式,选中
ages = titanic["Age"]

ages.head()
Out[5]: 
0    22.0
1    38.0
2    26.0
3    35.0
4    35.0
Name: Age, dtype: float64

DataFrame 的每列,都是 Series 类型, 我们可以用 type 检查一下:

type(titanic["Age"])
Out[6]: pandas.core.series.Series

我们也可以看一下它的 shape

titanic["Age"].shape
Out[7]: (891,)

DataFrame.shape 可以获取一个 DataFrame 或 Series 包含了多少行,多少列:(nrows, ncolumns),因为 Series 是一维数据类型,所以只返回多少行。

  1. 选择多列数据
age_sex = titanic[["Age", "Sex"]]

age_sex.head()
Out[9]: 
    Age     Sex
0  22.0    male
1  38.0  female
2  26.0  female
3  35.0  female
4  35.0    male

选择多列,将多个列名放到一个列表中,再用[]来选择。返回的数据,同样是一个DataFrame

更多索引信息:indexing and selecting data

过滤 DataFrame

Ques: 我想筛选年龄大于35的人

above_35 = titanic[titanic["Age"] > 35]

above_35.head()
Out[13]: 
    PassengerId  Survived  Pclass                                               Name     Sex  ...  Parch    Ticket     Fare Cabin  Embarked
1             2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ...      0  PC 17599  71.2833   C85         C
6             7         0       1                            McCarthy, Mr. Timothy J    male  ...      0     17463  51.8625   E46         S
11           12         1       1                           Bonnell, Miss. Elizabeth  female  ...      0    113783  26.5500  C103         S
13           14         0       3                        Andersson, Mr. Anders Johan    male  ...      5    347082  31.2750   NaN         S
15           16         1       2                   Hewlett, Mrs. (Mary D Kingcome)   female  ...      0    248706  16.0000   NaN         S

[5 rows x 12 columns]

将筛选条件放到[]中,就可以筛选满足条件的数据

筛选条件:titanic["Age"] > 35, 它用来检查哪些 Age 列哪些数据大于35。

titanic["Age"] > 35
Out[14]: 
0      False
1       True
2      False
3      False
4      False
       ...  
886    False
887    False
888    False
889    False
890    False
Name: Age, Length: 891, dtype: bool

上面用到了 '>' 符号,同样的:==, !=, <, <= 这些判断符号也能使用。返回 True 或 False。返回的结果,可以传递给[],用来筛选 True 的那些数据。

Ques: 筛选 2级和3级仓的乘客。

class_23 = titanic[titanic["Pclass"].isin([2, 3])]

class_23.head()
Out[17]: 
   PassengerId  Survived  Pclass                            Name     Sex   Age  SibSp  Parch            Ticket     Fare Cabin Embarked
0            1         0       3         Braund, Mr. Owen Harris    male  22.0      1      0         A/5 21171   7.2500   NaN        S
2            3         1       3          Heikkinen, Miss. Laina  female  26.0      0      0  STON/O2. 3101282   7.9250   NaN        S
4            5         0       3        Allen, Mr. William Henry    male  35.0      0      0            373450   8.0500   NaN        S
5            6         0       3                Moran, Mr. James    male   NaN      0      0            330877   8.4583   NaN        Q
7            8         0       3  Palsson, Master. Gosta Leonard    male   2.0      3      1            349909  21.0750   NaN        S

注:Pclass 是乘客的舱室类别。有1,2,3三个级别。这里筛选2,3两个级别的乘客。
isin(),和上面所说的 条件判断类似。如果当前 Pcalss 的值是2或3,则返回True。然后通过这种方法筛选。

上面的筛选语句,可以使用 | 连接符,等同于:

class_23 = titanic[(titanic["Pclass"] == 2) | (titanic["Pclass"] == 3)]

当多个条件连接时,必须要将每个条件用小括号'()' 括起来。| 代表 or,& 代表 and

Ques: 筛选非空的年龄的乘客

age_no_na = titanic[titanic["Age"].notna()]

age_no_na.head()
Out[21]: 
   PassengerId  Survived  Pclass                                               Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked
0            1         0       3                            Braund, Mr. Owen Harris    male  ...      0         A/5 21171   7.2500   NaN         S
1            2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ...      0          PC 17599  71.2833   C85         C
2            3         1       3                             Heikkinen, Miss. Laina  female  ...      0  STON/O2. 3101282   7.9250   NaN         S
3            4         1       1       Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  ...      0            113803  53.1000  C123         S
4            5         0       3                           Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S

[5 rows x 12 columns]

notna() 可以返回 True 如果当前行的数据不是空值(Null)的话。因此,也可以放到[]中来筛选数据。

怎么选中特定的行和列

Ques: 我想知道年龄大于35岁的人的名字

adult_names = titanic.loc[titanic["Age"] > 35, "Name"]

adult_names.head()
Out[24]: 
1     Cumings, Mrs. John Bradley (Florence Briggs Th...
6                               McCarthy, Mr. Timothy J
11                             Bonnell, Miss. Elizabeth
13                          Andersson, Mr. Anders Johan
15                     Hewlett, Mrs. (Mary D Kingcome) 
Name: Name, dtype: object

这里我们需要一次性找到年龄大于35的人,然后找到他们的名字。所以之前的方法适用了。我们需要使用 loc/iloc 操作符。当用这两个操作符时如:loc[condition, column]. 逗号之前是我们想要数据满足的条件,逗号之后是我们想要的列。
如果你想要多个列名,可以将多个列名写在一个列表中: titanic.loc[titanic["Age"] > 35, ["Name", "Pclass"]]

当使用列名,行标签或条件表达式时,使用 loc 操作符。在逗号的前后,你可以使用单独的标签、一个标签列表、标签切片、条件表达式、甚至一个 :, 使用冒号意味着你想要选择所有行或者列(类似列表切片).

Ques: 我想选中 10-25行,3-5列的数据

titanic.iloc[9:25, 2:5]
Out[25]: 
    Pclass                                 Name     Sex
9        2  Nasser, Mrs. Nicholas (Adele Achem)  female
10       3      Sandstrom, Miss. Marguerite Rut  female
11       1             Bonnell, Miss. Elizabeth  female
12       3       Saundercock, Mr. William Henry    male
13       3          Andersson, Mr. Anders Johan    male
..     ...                                  ...     ...
20       2                 Fynney, Mr. Joseph J    male
21       2                Beesley, Mr. Lawrence    male
22       3          McGowan, Miss. Anna "Annie"  female
23       1         Sloper, Mr. William Thompson    male
24       3        Palsson, Miss. Torborg Danira  female

[16 rows x 3 columns]

如果你想要基于数据在表格中的位置选择数据,使用 iloc 操作符。数据在表中的索引从0开始。

选中数据后,你还可以直接给选中的数据赋值:

titanic.iloc[0:3, 3] = "anonymous"

titanic.head()
Out[27]: 
   PassengerId  Survived  Pclass                                          Name     Sex  ...  Parch            Ticket     Fare Cabin  Embarked
0            1         0       3                                     anonymous    male  ...      0         A/5 21171   7.2500   NaN         S
1            2         1       1                                     anonymous  female  ...      0          PC 17599  71.2833   C85         C
2            3         1       3                                     anonymous  female  ...      0  STON/O2. 3101282   7.9250   NaN         S
3            4         1       1  Futrelle, Mrs. Jacques Heath (Lily May Peel)  female  ...      0            113803  53.1000  C123         S
4            5         0       3                      Allen, Mr. William Henry    male  ...      0            373450   8.0500   NaN         S

[5 rows x 12 columns]

REMEMBER

  1. 选择子集时,用 []

  2. 在选择子集的[]中,你可以使用一个单独的行/列标签、一个行/列标签的列表、标签切片、条件表达式、或者冒号

  3. 用行和列名作为选择工具时,用 loc

  4. 用基于表格的位置来选择某个子集区域时,用 iloc

  5. 在使用loc/iloc 时,可以给选定的区域赋值

4. pandas 作图

会用到 matplotlib 的一些功能,所以可以先去看一下 matplotlib

air_quality.csv

air_quality = pd.read_csv("data/air_quality_no2.csv", index_col=0, parse_dates=True)

air_quality.head()
Out[4]: 
                     station_antwerp  station_paris  station_london
datetime                                                           
2019-05-07 02:00:00              NaN            NaN            23.0
2019-05-07 03:00:00             50.5           25.0            19.0
2019-05-07 04:00:00             45.0           27.7            19.0
2019-05-07 05:00:00              NaN           50.4            16.0
2019-05-07 06:00:00              NaN           61.9             NaN

使用 index_col, parse_dates 参数,来定义将第一列作为索引(默认的行标签索引是0,1,...),然后将格式设置为时间格式。

怎么创建图表

Ques: 我想快速做个可视化图表

air_quality.plot()
Out[5]: <AxesSubplot:xlabel='datetime'>

使用 DataFrame时,pandas会默认为每一列创建一条线。

Ques: 只想画出某列的图表

air_quality["station_paris"].plot()
Out[6]: <AxesSubplot:xlabel='datetime'>

要画某一列数据,先选出子集,然后使用 plot(),就可以画出了。所以,plot() 可以应用于 DataFrame 和 Series

Question:想比较两个城市的二氧化氮值。

air_quality.plot.scatter(x="station_london", y="station_paris", alpha=0.5)
Out[7]: <AxesSubplot:xlabel='station_london', ylabel='station_paris'>

除了默认的折线图,还有一些其他图表类型:

[
    method_name
    for method_name in dir(air_quality.plot)
    if not method_name.startswith("_")
]

Out[8]: 
['area',
 'bar',
 'barh',
 'box',
 'density',
 'hexbin',
 'hist',
 'kde',
 'line',
 'pie',
 'scatter']

譬如,箱型图:

air_quality.plot.box()
Out[9]: <AxesSubplot:>

Question: 我想每个图表都有一个子图

axs = air_quality.plot.area(figsize=(12, 4), subplots=True)

subplots参数,可以让每个图表单独作为一个子图

Question: 我想更深入一些,保存下来图片

fig, axs = plt.subplots(figsize=(12, 4))

air_quality.plot.area(ax=axs)
Out[12]: <AxesSubplot:xlabel='datetime'>

axs.set_ylabel("NO$_2$ concentration")
Out[13]: Text(0, 0.5, 'NO$_2$ concentration')

fig.savefig("no2_concentrations.png")

fig, axs = plt.subplots(figsize=(12, 4))        # 创建一个空白的图表和坐标
air_quality.plot.area(ax=axs)                   # pandas 放置在作图区域
axs.set_ylabel("NO$_2$ concentration")          # 做任何 matplotlib 的操作
fig.savefig("no2_concentrations.png")           # 用 matplotlib 的操作来保存图片

REMEMBER

  1. plot.* 方法适用于 Series, DataFrames
  2. 默认下,每一列会作为一个不同的类型作图(line, boxplot,...)
  3. 任何 pandas 创建的作图,都是 Matplotlib 对象。

5. 根据已有数据,创建新列数据

怎么从已有数据,创建新列数据

Ques: 我想把 NO2 的数据,新加一列 mg/m3 的单位
把二氧化氮的数据,得出每立方米多少毫克(转换参数是 1.882)

air_quality["london_mg_per_cubic"] = air_quality["station_london"] * 1.882

air_quality.head()
Out[5]: 
                     station_antwerp  station_paris  station_london  london_mg_per_cubic
datetime                                                                                
2019-05-07 02:00:00              NaN            NaN            23.0               43.286
2019-05-07 03:00:00             50.5           25.0            19.0               35.758
2019-05-07 04:00:00             45.0           27.7            19.0               35.758
2019-05-07 05:00:00              NaN           50.4            16.0               30.112
2019-05-07 06:00:00              NaN           61.9             NaN                  NaN

想创建新列,把要创建的列名放到 [] 中。
我们上面用的是一列数据 * 1.882,它会自动把此列中的每一个元素都 * 1.882。 不用手动遍历此列的数据相乘。

Question:两个城市的比值

air_quality["ratio_paris_antwerp"] = (
    air_quality["station_paris"] / air_quality["station_antwerp"]
)


air_quality.head()
Out[7]: 
                     station_antwerp  station_paris  station_london  london_mg_per_cubic  ratio_paris_antwerp
datetime                                                                                                     
2019-05-07 02:00:00              NaN            NaN            23.0               43.286                  NaN
2019-05-07 03:00:00             50.5           25.0            19.0               35.758             0.495050
2019-05-07 04:00:00             45.0           27.7            19.0               35.758             0.615556
2019-05-07 05:00:00              NaN           50.4            16.0               30.112                  NaN
2019-05-07 06:00:00              NaN           61.9             NaN                  NaN                  NaN

同样的,两列相除,会自动把每行的两个数据相除,不用手动遍历相除。

同样的,其他算术符号(+,-,*,/) 或 逻辑运算符:(<, >, =, ...) 都可以进行元素级别的比较计算。譬如我们之前过滤数据时,也是这样。

Ques:我想重命名列名

air_quality_renamed = air_quality.rename(
    columns={
        "station_antwerp": "BETR801",
        "station_paris": "FR04014",
        "station_london": "London Westminster",
    }
)


air_quality_renamed.head()
Out[9]: 
                     BETR801  FR04014  London Westminster  london_mg_per_cubic  ratio_paris_antwerp
datetime                                                                                           
2019-05-07 02:00:00      NaN      NaN                23.0               43.286                  NaN
2019-05-07 03:00:00     50.5     25.0                19.0               35.758             0.495050
2019-05-07 04:00:00     45.0     27.7                19.0               35.758             0.615556
2019-05-07 05:00:00      NaN     50.4                16.0               30.112                  NaN
2019-05-07 06:00:00      NaN     61.9                 NaN                  NaN                  NaN

rename() 可以用在列名和行标签上。只要提供一个字典,它的键是原来的名字,值是新名字,就可以了。
df.rename(columns={...}, index={...},)

rename() 不仅可以作用于重命名,还可以接收一个函数作为参数,例如将所有列名变成小写字母:

air_quality_renamed = air_quality_renamed.rename(columns=str.lower)

air_quality_renamed.head()
Out[11]: 
                     betr801  fr04014  london westminster  london_mg_per_cubic  ratio_paris_antwerp
datetime                                                                                           
2019-05-07 02:00:00      NaN      NaN                23.0               43.286                  NaN
2019-05-07 03:00:00     50.5     25.0                19.0               35.758             0.495050
2019-05-07 04:00:00     45.0     27.7                19.0               35.758             0.615556
2019-05-07 05:00:00      NaN     50.4                16.0               30.112                  NaN
2019-05-07 06:00:00      NaN     61.9                 NaN                  NaN                  NaN

REMEMBER

  1. 创建新列,用: [列名]
  2. 操作都是基于单个元素的,不需要循环
  3. rename() 一个字典或函数来重命名列名和行标签。

6. 计算统计信息

Ques: 年龄平均值

titanic["Age"].mean()
Out[4]: 29.69911764705882

Ques: 中位数

同时计算年龄和票价的中位数

titanic[["Age", "Fare"]].median()
Out[5]: 
Age     28.0000
Fare    14.4542
dtype: float64

一些基础数据可以为多列数据同时计算出来,还记得之前用的 describe() 吗?

titanic[["Age", "Fare"]].describe()
Out[6]: 
              Age        Fare
count  714.000000  891.000000
mean    29.699118   32.204208
std     14.526497   49.693429
min      0.420000    0.000000
25%     20.125000    7.910400
50%     28.000000   14.454200
75%     38.000000   31.000000
max     80.000000  512.329200

不用预先定义的统计数据,我们可以自定义一些合计数据的组合,得出数据:

titanic.agg(
    {
        "Age": ["min", "max", "median", "skew"],
        "Fare": ["min", "max", "median", "mean"],
    }
)

Out[7]: 
              Age        Fare
min      0.420000    0.000000
max     80.000000  512.329200
median  28.000000   14.454200
skew     0.389108         NaN
mean          NaN   32.204208

数据分组统计

Question:男女乘客分别平均年龄

titanic[["Sex", "Age"]].groupby("Sex").mean()
Out[8]: 
              Age
Sex              
female  27.915709
male    30.726645

我们先获取 Sex,Age两列数据,然后按照 Sex 分组,最后求平均值。

上面的过程,可以作为一个通用模式:split-apply-combine pattern

分割-应用-组合 模式

  1. 分割 数据成为不同的分组
  2. 对每个组,都应用一个方法
  3. 组合所有的结果

在pandas中,应用-组合 这两个步骤,通常一起做了。

上面的例子,我们明确的选中了 Sex, Age两列,如果不选中这两列。mean() 会对所有数字类型的列进行求平均值:

titanic.groupby("Sex").mean()
Out[9]: 
        PassengerId  Survived    Pclass        Age     SibSp     Parch       Fare
Sex                                                                              
female   431.028662  0.742038  2.159236  27.915709  0.694268  0.649682  44.479818
male     454.147314  0.188908  2.389948  30.726645  0.429809  0.235702  25.523893

针对已经分组后的数据,也可以使用 [] 来获取某列数据,然后求平均值:

titanic.groupby("Sex")["Age"].mean()
Out[10]: 
Sex
female    27.915709
male      30.726645
Name: Age, dtype: float64

Pclass这列数据,代表的是座舱的等级,譬如1,2,3三个等级。对它求平均值没有什么意义,所以 pandas 有个数据类型:Categorical, 代表一个类别。

Question: 联合分组。怎么针对 Sex,Pclass进行分组,查看每组的票价平均值?

at is the mean ticket fare price for each of the sex and cabin class combinations?

titanic.groupby(["Sex", "Pclass"])["Fare"].mean()
Out[11]: 
Sex     Pclass
female  1         106.125798
        2          21.970121
        3          16.118810
male    1          67.226127
        2          19.741782
        3          12.661633
Name: Fare, dtype: float64

分组可以接收多个参数,只要把多个参数放到列表里面就行了。

按类别统计次数

Question:每一个座舱类型,有多少个乘客?

titanic["Pclass"].value_counts()
Out[12]: 
3    491
1    216
2    184
Name: Pclass, dtype: int64

value_counts() 可以统计每个类别有多少记录

上面的例子,其实是分组操作的一个快捷方式,完整的方式如下:

titanic.groupby("Pclass")["Pclass"].count()
Out[13]: 
Pclass
1    216
2    184
3    491
Name: Pclass, dtype: int64

size 和 count 两个方法,都可以在分组中使用,但是 size 包含空值记录(表格的大小),count 方法不包含空值。

REMEMBER

统计数据可以作用于所有行和列
分组功能提供了 split-apply-combine pattern
value_counts() 是一个快捷方式来计算一个类别下不同值的个数。

7. 改变表格布局

对行排序

Ques:我想对年龄进行排序

titanic.sort_values(by="Age").head()
Out[6]: 
     PassengerId  Survived  Pclass                             Name     Sex   Age  SibSp  Parch  Ticket     Fare Cabin Embarked
803          804         1       3  Thomas, Master. Assad Alexander    male  0.42      0      1    2625   8.5167   NaN        C
755          756         1       2        Hamalainen, Master. Viljo    male  0.67      1      1  250649  14.5000   NaN        S
644          645         1       3           Baclini, Miss. Eugenie  female  0.75      2      1    2666  19.2583   NaN        C
469          470         1       3    Baclini, Miss. Helene Barbara  female  0.75      2      1    2666  19.2583   NaN        C
78            79         1       2    Caldwell, Master. Alden Gates    male  0.83      0      2  248738  29.0000   NaN        S

Ques: 我想对座舱等级,年龄进行降序排序(先对座舱等级排序,然后对年龄排序)

titanic.sort_values(by=['Pclass', 'Age'], ascending=False).head()
Out[7]: 
     PassengerId  Survived  Pclass                       Name     Sex   Age  SibSp  Parch  Ticket    Fare Cabin Embarked
851          852         0       3        Svensson, Mr. Johan    male  74.0      0      0  347060  7.7750   NaN        S
116          117         0       3       Connors, Mr. Patrick    male  70.5      0      0  370369  7.7500   NaN        Q
280          281         0       3           Duane, Mr. Frank    male  65.0      0      0  336439  7.7500   NaN        Q
483          484         1       3     Turkula, Mrs. (Hedwig)  female  63.0      0      0    4134  9.5875   NaN        S
326          327         0       3  Nysveen, Mr. Johan Hansen    male  61.0      0      0  345364  6.2375   NaN        S

注意:排序完成后,行标签(索引)也会跟着被打乱(排序了嘛),如果想要恢复原来的顺序,可以通过标签重新排序。sorting data

pivot

太费脑细胞。略。
两个链接:
链接1
链接2

8. 多表联合数据

air_quality_no2_long.csv

air_quality_no2 = pd.read_csv("data/air_quality_no2_long.csv",
                              parse_dates=True)


air_quality_no2 = air_quality_no2[["date.utc", "location",
                                   "parameter", "value"]]


air_quality_no2.head()
Out[4]: 
                    date.utc location parameter  value
0  2019-06-21 00:00:00+00:00  FR04014       no2   20.0
1  2019-06-20 23:00:00+00:00  FR04014       no2   21.8
2  2019-06-20 22:00:00+00:00  FR04014       no2   26.5
3  2019-06-20 21:00:00+00:00  FR04014       no2   24.9
4  2019-06-20 20:00:00+00:00  FR04014       no2   21.4

air_quality_pm25_long.csv

air_quality_pm25 = pd.read_csv("data/air_quality_pm25_long.csv",
                               parse_dates=True)


air_quality_pm25 = air_quality_pm25[["date.utc", "location",
                                     "parameter", "value"]]


air_quality_pm25.head()
Out[7]: 
                    date.utc location parameter  value
0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5

合并两个表

Ques: 想要将两个表,合并到一张表(把一张表,追加到另一张表底下)

air_quality = pd.concat([air_quality_pm25, air_quality_no2], axis=0)

air_quality.head()
Out[9]: 
                    date.utc location parameter  value
0  2019-06-18 06:00:00+00:00  BETR801      pm25   18.0
1  2019-06-17 08:00:00+00:00  BETR801      pm25    6.5
2  2019-06-17 07:00:00+00:00  BETR801      pm25   18.5
3  2019-06-17 06:00:00+00:00  BETR801      pm25   16.0
4  2019-06-17 05:00:00+00:00  BETR801      pm25    7.5

concat() 可以将两张表合并到一张表。axis 可以设置方向(行方向或列方向),0 代表追加到下方(竖直方向)。1代表追加到右边(水平方向),默认 axis 是0

我们对此表排序一下,可以看出 parameter 列中有 no2 和 pm25 两种类型的值,说明确实合并了:

air_quality = air_quality.sort_values("date.utc")

air_quality.head()
Out[14]: 
                       date.utc            location parameter  value
2067  2019-05-07 01:00:00+00:00  London Westminster       no2   23.0
1003  2019-05-07 01:00:00+00:00             FR04014       no2   25.0
100   2019-05-07 01:00:00+00:00             BETR801      pm25   12.5
1098  2019-05-07 01:00:00+00:00             BETR801       no2   50.5
1109  2019-05-07 01:00:00+00:00  London Westminster      pm25    8.0

以下内容是译者自己补充的例子,非翻译的文档内容。

Ques: 通过某列合并(有点像 vlookup,或者 SQL 的多表联合查询)

现有三张表:

>>> d1
    name  age
0   wang   23
1  zhang   35
2     li   23

>>> d2
    name  age    addr
0   wang  100  ssssss
1  zhang  100   kkkkk
2     li  100  llllll
3    any  100     100


>>> d3
   names  age
0   wang   22
1  huang   23

我们选择 d1, d2 两表 name 列的交集数据:

>>> pd.merge(d1,d2,on='name')
    name  age_x  age_y    addr
0   wang     23    100  ssssss
1  zhang     35    100   kkkkk
2     li     23    100  llllll

on="name" 意思是以两张表共有的 name 列为主键,进行两张表的关联, age_x, age_y 代表两张表的 age,因为两张表都有age列,所以合并后都带了一个后缀。默认on=None,则会以两张表共有的所有列为联合键进行关联,相当于:on=['name','age']

如果列名不一致:d3 的名字列叫'names', d2 的叫'name',两者不一样,也能合并:

>>> pd.merge(d1,d3,left_on='name',right_on='names')
   name  age_x names  age_y
0  wang     23  wang     22

left_on, right_on,分别指定两个表的联合键,然后合并两张表主键的交集部分。

上面说的都是求两个表的交集部分,如果我们想要两个表的联合部分(并集),或者只想要左侧表的全部,怎么办?
how 参数,可以指定怎么选取两张表的数据,可选值有:'left', 'right', 'outer', 'inner',默认'inner'

取 d1, d2 两张表的姓名列的并集:

>>> pd.merge(d1,d2,how='outer',on='name')
    name  age_x  age_y    addr
0   wang   23.0    100  ssssss
1  zhang   35.0    100   kkkkk
2     li   23.0    100  llllll
3    any    NaN    100     100

姓名列的并集选出来了,没有的数据,会以 NaN 填充。譬如来自 d3 表的 any,它没有 age_x(来自d2的age),age_x 会是 NaN

同样,只想要左侧 d1 表的所有人名,不要 d2 的人名:

>>> pd.merge(d1,d2,how='left',on='name')
    name  age_x  age_y    addr
0   wang     23    100  ssssss
1  zhang     35    100   kkkkk
2     li     23    100  llllll

Ques: 想要按照行索引来关联数据,怎么办

>>> pd.merge(d2,d3,left_index=True,right_index=True)
    name  age_x    addr  names  age_y
0   wang    100  ssssss   wang     22
1  zhang    100   kkkkk  huang     23

left_index,right_index。以两张表的行标签为联合键,对齐两张表,参数默认值是 False。此例子中,行标签就是索引 0,1,2,...

Question:我想知道哪些数据是左表独有的,哪些数据是右表独有的,哪些数据是共有的?

>>> pd.merge(d1,d2,indicator=True,how='outer',on='name')
    name  age_x  age_y    addr      _merge
0   wang   23.0    100  ssssss        both
1  zhang   35.0    100   kkkkk        both
2     li   23.0    100  llllll        both
3    any    NaN    100     100  right_only

indicator 参数,默认是 False。它会新增一列 _merge 数据,显示的值有三种:both,left_only, right_only,代表:共有,只在左表,只在右表

怎么检查两个表的数据,是否一一对应?(譬如一个名字对一个名字)

我们对 d1 表,新增一个重复的人名:wang

>>> d1
    name  age
0   wang   23
1  zhang   35
2     li   23
3   wang   25
>>> pd.merge(d1,d2,on='name',validate='one_to_one')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\py3.9\lib\site-packages\pandas\core\reshape\merge.py", line 74, in merge
    op = _MergeOperation(
  File "C:\py3.9\lib\site-packages\pandas\core\reshape\merge.py", line 678, in __init__
    self._validate(validate)
  File "C:\py3.9\lib\site-packages\pandas\core\reshape\merge.py", line 1362, in _validate
    raise MergeError(
pandas.errors.MergeError: Merge keys are not unique in left dataset; not a one-to-one merge

validate 参数,我们指定了数据要 一对一;即左边一个人名,对应右表一个人名。因为我们左表有两个重复的人名,所以报错了。

好了,基本上所有的参数都讲完了。

pd.merge(
    left,  # 左表,DataFrame 或 Series
    right,  # 右表,DataFrame 或 Series
    how="inner",  # 连接方式:inner,outer,left,right
    on=None,  # 连接用到的联合键;可以是列名或索引名,但是必须要同时在两个表都存在,默认None时,会用两张表所有共有的列作为联合主键。
    left_on=None,  # 单独指定左表用到的键
    right_on=None,  # 右表用到的键
    left_index=False,  # True 时,以行索引来关联(一行对一行)
    right_index=False,  # 同上
    sort=True,  # 根据联合键来对结果排序。默认 True
    suffixes=("_x", "_y"),  # 两个表存在同名列时,结果中会加个后缀区分哪个来自哪列。
    copy=True,  # 不清楚。
    indicator=False,  # 指示器,显示哪些数据是独有的,或是共有的。
    validate=None,  # 四个参数:'one_to_one', 'one_to_many','many_to_one','many_to_many'. 分别代表:检查两个都没有重复值。检查左表没有重复值。检查右表没有重复值。允许有重复值。
)

9. 处理时间

raw data

air_quality = pd.read_csv("data/air_quality_no2_long.csv")

air_quality = air_quality.rename(columns={"date.utc": "datetime"})

air_quality.head()
Out[5]: 
    city country                   datetime location parameter  value   unit
0  Paris      FR  2019-06-21 00:00:00+00:00  FR04014       no2   20.0  µg/m³
1  Paris      FR  2019-06-20 23:00:00+00:00  FR04014       no2   21.8  µg/m³
2  Paris      FR  2019-06-20 22:00:00+00:00  FR04014       no2   26.5  µg/m³
3  Paris      FR  2019-06-20 21:00:00+00:00  FR04014       no2   24.9  µg/m³
4  Paris      FR  2019-06-20 20:00:00+00:00  FR04014       no2   21.4  µg/m³

pandas datatime 属性

air_quality["datetime"] = pd.to_datetime(air_quality["datetime"])

air_quality["datetime"]
Out[8]: 
0      2019-06-21 00:00:00+00:00
1      2019-06-20 23:00:00+00:00
2      2019-06-20 22:00:00+00:00
3      2019-06-20 21:00:00+00:00
4      2019-06-20 20:00:00+00:00
                  ...           
2063   2019-05-07 06:00:00+00:00
2064   2019-05-07 04:00:00+00:00
2065   2019-05-07 03:00:00+00:00
2066   2019-05-07 02:00:00+00:00
2067   2019-05-07 01:00:00+00:00
Name: datetime, Length: 2068, dtype: datetime64[ns, UTC]

to_datetime() 可以很轻松的将字符串类型的时间列,转换成时间格式,类似于 datetime.datetime
在读取文件如:read_csv(),read_json()时,可以将 列名 传递给参数 parse_dates,在读取时,就会将此列转换成时间:
pd.read_csv("../data/air_quality_no2_long.csv", parse_dates=["datetime"])

pandas.Timestamp 还可以进行日期计算:

air_quality["datetime"].min(), air_quality["datetime"].max()
Out[9]: 
(Timestamp('2019-05-07 01:00:00+0000', tz='UTC'),
 Timestamp('2019-06-21 00:00:00+0000', tz='UTC'))

 air_quality["datetime"].max() - air_quality["datetime"].min()
Out[10]: Timedelta('44 days 23:00:00')

更多时间概念

Ques: 新增一列只包含月份

air_quality["month"] = air_quality["datetime"].dt.month

air_quality.head()
Out[12]: 
    city country                  datetime location parameter  value   unit  month
0  Paris      FR 2019-06-21 00:00:00+00:00  FR04014       no2   20.0  µg/m³      6
1  Paris      FR 2019-06-20 23:00:00+00:00  FR04014       no2   21.8  µg/m³      6
2  Paris      FR 2019-06-20 22:00:00+00:00  FR04014       no2   26.5  µg/m³      6
3  Paris      FR 2019-06-20 21:00:00+00:00  FR04014       no2   24.9  µg/m³      6
4  Paris      FR 2019-06-20 20:00:00+00:00  FR04014       no2   21.4  µg/m³      6

除了 month, 还有 year, weekofyear, quarter,weekday,hour 可以使用,只需要通过 dt 来访问。

Ques: 按照每天和地点分组,求每个组的平均值

air_quality.groupby(
    [air_quality["datetime"].dt.weekday, "location"])["value"].mean()

Out[13]: 
datetime  location          
0         BETR801               27.875000
          FR04014               24.856250
          London Westminster    23.969697
1         BETR801               22.214286
          FR04014               30.999359
                                  ...    
5         FR04014               25.266154
          London Westminster    24.977612
6         BETR801               21.896552
          FR04014               23.274306
          London Westminster    24.859155
Name: value, Length: 21, dtype: float64

weekday 可以获取日期是周几。0代表周一,6代表周日。

datetime 作为 索引

no_2 = air_quality.pivot(index="datetime", columns="location", values="value")

no_2.head()
Out[19]: 
location                   BETR801  FR04014  London Westminster
datetime                                                       
2019-05-07 01:00:00+00:00     50.5     25.0                23.0
2019-05-07 02:00:00+00:00     45.0     27.7                19.0
2019-05-07 03:00:00+00:00      NaN     50.4                19.0
2019-05-07 04:00:00+00:00      NaN     61.9                16.0
2019-05-07 05:00:00+00:00      NaN     72.4                 NaN

pivot 可以重塑表格的形状(行转换成列,列转换成行等),此处把 datetime 列转换成了索引。通常来说,可以使用 set_index 方法来将列转换成索引。

时间索引,可以不再通过 dt 来访问一些属性,而是通过 index 直接访问:

no_2.index.year, no_2.index.weekday
Out[20]: 
(Int64Index([2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019,
             ...
             2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019, 2019],
            dtype='int64', name='datetime', length=1033),
 Int64Index([1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
             ...
             3, 3, 3, 3, 3, 3, 3, 3, 3, 4],
            dtype='int64', name='datetime', length=1033))

Ques:选取特定时间段的数据:

no_2["2019-05-20":"2019-05-21"].plot();

时间频率转换采样

将时间转换成其他频率:如小时转换成天。下面的例子将时间按照月重新采样,然后求最大值

monthly_max = no_2.resample("M").max()

monthly_max
Out[23]: 
location                   BETR801  FR04014  London Westminster
datetime                                                       
2019-05-31 00:00:00+00:00     74.5     97.0                97.0
2019-06-30 00:00:00+00:00     52.5     84.7                52.0

resample() 有点像分组操作:

  1. 它提供了一个时间分组,用字符串('M','5H','D', ...) 来定义目标频率
  2. 需要一个聚合函数:max, mean, ...

我们可以使用 freq 属性来查询它的频率

monthly_max.index.freq
Out[24]: <MonthEnd>

REMEMBER

  1. 有效的时间字符串可以用:to_datetime 方法来转换成时间格式。也可以在读取文件时转换成。
  2. Datetime 对象支持计算,逻辑操作,使用 dt 访问器还有一些其他方便的时间相关的属性
  3. 一个时间索引(DatetimeIndex) 包含了时间相关的属性,并支持方便的切片
  4. 对时间重新采样可以方便的转换时间频率

10. 操作文本数据

titanic.csv

Question:将人名转换成小写字符

titanic["Name"].str.lower()
Out[4]: 
0                                braund, mr. owen harris
1      cumings, mrs. john bradley (florence briggs th...
2                                 heikkinen, miss. laina
3           futrelle, mrs. jacques heath (lily may peel)
4                               allen, mr. william henry
                             ...                        
886                                montvila, rev. juozas
887                         graham, miss. margaret edith
888             johnston, miss. catherine helen "carrie"
889                                behr, mr. karl howell
890                                  dooley, mr. patrick
Name: Name, Length: 891, dtype: object

获取 Name 列的切片,然后通过 str 访问并应用 lower() 方法,将此列转换成小写。

Ques: 字符串分割后,放到新列?

titanic["Name"].str.split(",")
Out[5]: 
0                             [Braund,  Mr. Owen Harris]
1      [Cumings,  Mrs. John Bradley (Florence Briggs ...
2                              [Heikkinen,  Miss. Laina]
3        [Futrelle,  Mrs. Jacques Heath (Lily May Peel)]
4                            [Allen,  Mr. William Henry]
                             ...                        
886                             [Montvila,  Rev. Juozas]
887                      [Graham,  Miss. Margaret Edith]
888          [Johnston,  Miss. Catherine Helen "Carrie"]
889                             [Behr,  Mr. Karl Howell]
890                               [Dooley,  Mr. Patrick]
Name: Name, Length: 891, dtype: object

Series.str.split() 可以将每个值都按照分隔符号进行分割,返回列表。

titanic["Surname"] = titanic["Name"].str.split(",").str.get(0)

titanic["Surname"]
Out[7]: 
0         Braund
1        Cumings
2      Heikkinen
3       Futrelle
4          Allen
         ...    
886     Montvila
887       Graham
888     Johnston
889         Behr
890       Dooley
Name: Surname, Length: 891, dtype: object

Series.str.get() 可以提取分割后列表中的某个值。没错,str.split() 可以和 str.get() 连接在一起使用。
字符串分割替换

Ques: 是否包含某个字符?

titanic["Name"].str.contains("Countess")
Out[8]: 
0      False
1      False
2      False
3      False
4      False
       ...  
886    False
887    False
888    False
889    False
890    False
Name: Name, Length: 891, dtype: bool

contains() 可以对每个元素判断是否存在某个字符。返回的值是 True 或 False,我们可以把它放到 [] 中进行筛选,获取子集。(此函数可以使用正则表达式)

titanic[titanic["Name"].str.contains("Countess")]
Out[9]: 
     PassengerId  Survived  Pclass                                               Name     Sex   Age  ...  Parch  Ticket  Fare  Cabin Embarked Surname
759          760         1       1  Rothes, the Countess. of (Lucy Noel Martha Dye...  female  33.0  ...      0  110152  86.5    B77        S  Rothes

[1 rows x 13 columns]

还有一个方法 extract(), 可以直接提取满足条件的内容。

Question:字符串长度

titanic["Name"].str.len()
Out[10]: 
0      23
1      51
2      22
3      44
4      24
       ..
886    21
887    28
888    40
889    21
890    19
Name: Name, Length: 891, dtype: int64

和 python 的方法类似。len() 可以获取数据集内每个字符串的长度。

titanic["Name"].str.len().idxmax()
Out[11]: 307

idxmax() 方法,获取最大值所在的行标签。它不是一个字符串方法。

titanic.loc[titanic["Name"].str.len().idxmax(), "Name"]
Out[12]: 'Penasco y Castellana, Mrs. Victor de Satode (Maria Josefa Perez de Soto y Vallejo)'

根据row(307), column(Name), 使用 loc 操作符,定位到某个值。

Question:字符串替换

titanic["Sex_short"] = titanic["Sex"].replace({"male": "M", "female": "F"})

titanic["Sex_short"]
Out[14]: 
0      M
1      F
2      F
3      F
4      M
      ..
886    M
887    F
888    F
889    M
890    M
Name: Sex_short, Length: 891, dtype: object

使用 replace() 方法,将某个字符串,替换成另一个字符串。将原值作为键,新值作为值,放到字典中,传递给 replace({from : to})

REMEMBER

  1. 字符串方法,可以使用 str 访问器
  2. 字符串方法都是元素级别的,可以作为条件判断索引进行筛选子集
  3. replace 方法可以接收字典来方便的替换值。

compare to Excel

url = (
    "https://raw.github.com/pandas-dev"
    "/pandas/master/pandas/tests/io/data/csv/tips.csv"
)


tips = pd.read_csv(url)

tips
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
..          ...   ...     ...    ...   ...     ...   ...
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]

筛选:略。上文有

只选择某几列:

tips[["sex", "total_bill", "tip"]]

if语句
=IF(A1<60,"不及格","及格")

tips["bucket"] = np.where(tips["total_bill"] < 10, "low", "high")

tips
Out[19]: 
     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]

where(condition, 'true_msg', 'false_msg') 可以做到类似 IF 语句。

日期操作:

tips["date1"] = pd.Timestamp("2013-01-15")  # 日期格式时间

tips["date2"] = pd.Timestamp("2015-02-15")

tips["date1_year"] = tips["date1"].dt.year  # 获取日期的年份

tips["date2_month"] = tips["date2"].dt.month  # 获取日期的月份

tips["date1_next"] = tips["date1"] + pd.offsets.MonthBegin()  # 获取下个月这天的日期

tips["months_between"] = tips["date2"].dt.to_period("M") - tips["date1"].dt.to_period("M")  # 两个月份之间的时间差; dt.to_period("M") 会返回到月份 2013-01


tips[
    ["date1", "date2", "date1_year", "date2_month", "date1_next", "months_between"]
]

Out[26]: 
         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]

删除一列:

tips.drop("sex", axis=1)
Out[28]: 
     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]

drop(列名,axis=1); axis=1 代表列,水平方向

删除一行:

tips.drop(0, axis=0)

drop(行索引, axis=0); axis=0 代表行

新增一行:

df
Out[67]: 
  class  student_count all_pass
0     A             42      Yes
1     A             35      Yes
2     A             42      Yes
3     B             50       No
4     C             47       No
5     D             45      Yes

new_row = {"class": "E", "student_count": 51, "all_pass": True}

df.append(new_row, ignore_index=True)
Out[69]: 
  class  student_count all_pass
0     A             42      Yes
1     A             35      Yes
2     A             42      Yes
3     B             50       No
4     C             47       No
5     D             45      Yes
6     E             51     True

支持 DataFrame/Series/字典,或者这三种数据类型的列表

重命名列名:

tips.rename(columns={"total_bill": "total_bill_2"})
Out[29]: 
     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]

rename(columns={'old name': 'new name', ...})

排序

tips = tips.sort_values(["sex", "total_bill"])

tips
Out[31]: 
     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]

sort_values 可以对单个或一组标签或列名排序. ascending 参数可以控制升序或降序(默认 True 升序), inplace 参数可以控制是否直接在原数据上更改(默认 False)

去除文本右边空格

tips["time"].str.rstrip()

查找关键字位置:

tips["sex"].str.find("ale")
Out[34]: 
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

找不到关键字,返回 -1

截取字符串

tips["sex"].str[0:1]
Out[35]: 
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

分割字符串

>>> s = pd.DataFrame({"name":["wang tao", "zhang fei"]})
>>> s
        name
0   wang tao
1  zhang fei

>>> s['name'].str.split(' ')
0     [wang, tao]
1    [zhang, fei]
Name: name, dtype: object

>>> s['name'].str.split(' ',expand=True)
       0    1
0   wang  tao
1  zhang  fei

split() 函数可以分割每个元素成一个列表。expand=True 可以让分割后的数据变成 DataFrame 对象。

字符串分割成列:

firstlast = pd.DataFrame({"String": ["John Smith", "Jane Cook"]})

firstlast["First_Name"] = firstlast["String"].str.split(" ", expand=True)[0]

firstlast["Last_Name"] = firstlast["String"].str.rsplit(" ", expand=True)[0]

firstlast
Out[39]: 
       String First_Name Last_Name
0  John Smith       John      John
1   Jane Cook       Jane      Jane

DateFrame[column_index] 可以选取通过列索引选取某列数据

大小写转换:

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

firstlast["upper"] = firstlast["string"].str.upper()

firstlast["lower"] = firstlast["string"].str.lower()

firstlast["title"] = firstlast["string"].str.title()

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

vlookup 类似合并,见上文:多表联合。

数据填充:

df = pd.DataFrame({"AAA": [1] * 8, "BBB": list(range(0, 8))})

df
Out[58]: 
   AAA  BBB
0    1    0
1    1    1
2    1    2
3    1    3
4    1    4
5    1    5
6    1    6
7    1    7

series = list(range(1, 5))

series
Out[60]: [1, 2, 3, 4]

df.loc[2:5, "AAA"] = series

df
Out[62]: 
   AAA  BBB
0    1    0
1    1    1
2    1    2
3    2    3
4    3    4
5    4    5
6    1    6
7    1    7

数据去重:

df = pd.DataFrame(
    {
        "class": ["A", "A", "A", "B", "C", "D"],
        "student_count": [42, 35, 42, 50, 47, 45],
        "all_pass": ["Yes", "Yes", "Yes", "No", "No", "Yes"],
    }
)


df.drop_duplicates()
Out[64]: 
  class  student_count all_pass
0     A             42      Yes
1     A             35      Yes
3     B             50       No
4     C             47       No
5     D             45      Yes

df.drop_duplicates(["class", "student_count"])
Out[65]: 
  class  student_count all_pass
0     A             42      Yes
1     A             35      Yes
3     B             50       No
4     C             47       No
5     D             45      Yes

drop_duplicates(subset, keep, inplace) 有三个主要参数:

subset: 依据哪些列进行去重,默认所有列。也可以指定列(如上例所示)

keep: 有三个可选的值:{‘first’, ‘last’, False}, 默认 ‘first’;也就是说遇到重复值,留下哪一个,默认留下第一个, False 代表删除所有的重复值

inplace: bool,默认 False。是否直接在原数据上更改,还是返回一个全新复制一份的DataFrame

数据透视表:


类似操作:

pd.pivot_table(
    tips, values="tip", index=["size"], columns=["sex"], aggfunc=np.average
)

Out[66]: 
sex     Female      Male
size                    
1     1.276667  1.920000
2     2.528448  2.614184
3     3.250000  3.476667
4     4.021111  4.172143
5     5.140000  3.750000
6     4.600000  5.850000

看完觉得还不够?官网用户指导

posted @ 2021-10-11 13:55  wztshine  阅读(305)  评论(0编辑  收藏  举报