datawhale8月组队学习《pandas数据处理与分析》(下)(文本、分类、时序数据)

文章目录

*
第八章 文本数据

+ 8.1 str对象
+
* 8.1.1 str对象的设计意图
* 8.1.3 string类型
+ 8.2 正则表达式基础
+
* 8.2.1 . 一般字符的匹配
* 8.2.2 元字符基础
* 8.2.3 简写字符集
+ 8.3 文本处理的五类操作
+
* 8.3.1 str.split拆分
* 8.3.2 str.joinstr.cat合并
* 8.3.3 匹配
* 8.3.5 提取
+ 8.4、常用字符串函数
+
* 8.4.1 字母型函数
* 8.4.2 数值型函数
* 8.4.3 统计型函数
* 8.4.4 格式型函数
+ 8.5 练习
+
* Ex1:房屋信息数据集
* Ex2:《权力的游戏》剧本数据集
第九章 分类数据

+ 9.1 cat对象
+
* 9.1.1 cat对象的属性
* 9.1.2 类别的增加、删除和修改
+ 9.2 有序分类
+
* 9.2.1 序的建立
* 9.2.2 排序和比较
+ 9.3 区间类别
+
* 9.3.1 利用cut和qcut进行区间构造
* 9.3.2 一般区间的构造
* 9.3.3 区间的属性与方法
+ 9.4 练习
+
* Ex1: 统计未出现的类别
* Ex2: 钻石数据集
第十章 时序数据

+ 10.1 时序中的基本对象
+ 10.2 时间戳
+
* 10.2.1 Timestamp的构造与属性
* 10.2.2 Datetime序列的生成
* 10.2.3 dt对象
* 10.2.4 时间戳的切片与索引
+ 10.3 时间差
+
* 10.3.1 Timedelta的生成
* 10.2.2 Timedelta的运算
+ 10.4 日期偏置
+
* 10.4.1 Offset对象
* 10.4.2 偏置字符串
+ 10.5、时序中的滑窗与分组
+
* 10.5.1 滑动窗口
* 10.5.2 重采样
+ 10.6 练习
+
* Ex1:太阳辐射数据集
* Ex2:水果销量数据集

课程资料《pandas数据处理与分析》github地址讲解视频习题参考答案pandas官网
传送门:

第八章 文本数据

8.1 str对象

8.1.1 str对象的设计意图

str对象是定义在 IndexSeries上的属性,专门用于处理每个元素的文本内容,其内部定义了大量方法,因此对一个序列进行文本处理,首先需要获取其 str对象。在Python标准库中也有 str 模块,为了使用上的便利,在 pandas 的50个 str 对象方法中,有31个是和标准库中的 str 模块方法同名且功能一致,例如字母转为大写的操作:

var = 'abcd'
str.upper(var)
Out[4]: 'ABCD'

s = pd.Series(['abcd', 'efg', 'hi'])

s.str
Out[6]: <pandas.core.strings.accessor.StringMethods at 0x2b796892d60>

s.str.upper()
Out[7]:
0    ABCD
1     EFG
2      HI
dtype: object

8.1.2 []索引器
对于 str 对象而言,可理解为其对字符串进行了序列化的操作,例如在一般的字符串中,通过 [] 可以取出某个位置的元素,同时也能通过切片得到子串。
pandas中过对 str 对象使用 [] 索引器,可以完成完全一致的功能,并且如果超出范围则返回缺失值:

s.str[0]
Out[10]:
0    a
1    e
2    h
dtype: object

s.str[-1: 0: -2]
Out[11]:
0    db
1     g
2     i
dtype: object

s.str[2]
Out[12]:
0      c
1      g
2    NaN
dtype: object
import numpy as np
import pandas as pd

s = pd.Series(['abcd', 'efg', 'hi'])
s.str[0]
0    a
1    e
2    h
dtype: object

8.1.3 string类型

在上一章提到,从 pandas 的 1.0.0 版本开始,引入了 string 类型,其引入的动机在于:原来所有的字符串类型都会以 object 类型的 Series 进行存储,但 object 类型只应当存储混合类型,例如同时存储浮点、字符串、字典、列表、自定义类型等,因此字符串有必要同数值型或 category 一样,具有自己的数据存储类型,从而引入了 string 类型。
总体上说,绝大多数对于 object 和 string 类型的序列使用 str 对象方法产生的结果是一致,但是在下面提到的两点上有较大差异:

  1. 二者对于某些对象的 str 序列化方法不同。
    可迭代(Iterable)对象包括但不限于字符串、字典、列表。对于一个可迭代对象, string 类型和 object类型对它们的序列化方式不同,序列化后 str对象返回结果也可能不同。例如:
s = pd.Series([{1: 'temp_1', 2: 'temp_2'}, ['a', 'b'], 0.5, 'my_string'])
s
0    {1: 'temp_1', 2: 'temp_2'}
1                        [a, b]
2                           0.5
3                     my_string
dtype: object
s.str[1]
0    temp_1
1         b
2       NaN
3         y
dtype: object
s.astype('string').str[1]
0    1
1    '
2    .
3    y
dtype: string

除了最后一个字符串元素,前三个元素返回的值都不同,其原因在于:

  • 当序列类型为 object 时,是对于每一个元素进行 [] 索引,因此对于字典而言,返回temp_1字符串,对于列表则返回第二个值,而第三个为不可迭代对象,返回缺失值,第四个是对字符串进行 [] 索引。
  • string 类型的 str 对象先把整个元素转为字面意义的字符串,例如对于列表而言,第一个元素即 “{“,而对于最后一个字符串元素而言,恰好转化前后的表示方法一致,因此结果和 object 类型一致。

  • string 类型是 Nullable 类型,但 object 不是
    这意味着 string 类型的序列,如果调用的 str 方法返回值为整数 Series 和布尔 Series 时,其分别对应的 dtype 是 Int 和 boolean 的 Nullable 类型,而 object 类型则会分别返回 int/float 和 bool/object ,不过这取决于缺失值的存在与否。
    同时,字符串的比较操作,也具有相似的特性, string 返回 Nullable 类型,但 object 不会。

s = pd.Series(['a'])

s.str.len()
Out[17]:
0    1
dtype: int64

s.astype('string').str.len()
Out[18]:
0    1
dtype: Int64

s == 'a'
Out[19]:
0    True
dtype: bool

s.astype('string') == 'a'
Out[20]:
0    True
dtype: boolean

s = pd.Series(['a', np.nan])

s.str.len()
Out[22]:
0    1.0
1    NaN
dtype: float64

s.astype('string').str.len()
Out[23]:
0       1
1    <NA>
dtype: Int64

s == 'a'
Out[24]:
0     True
1    False
dtype: bool

s.astype('string') == 'a'
Out[25]:
0    True
1    <NA>
dtype: boolean

对于全体元素为数值类型的序列,即使其类型为 object 或者 category 也不允许直接使用 str 属性。如果需要把数字当成 string 类型处理,可以使用 astype 强制转换为 string 类型的 Series :

s = pd.Series([12, 345, 6789])

s.astype('string').str[1]
Out[27]:
0    2
1    4
2    7
dtype: string

8.2 正则表达式基础

这一节的两个表格来自于 learn-regex-zh 这个关于正则表达式项目,其使用 MIT 开源许可协议。这里只是介绍正则表达式的基本用法,需要系统学习的读者可参考《Python3 正则表达式》,或者《 正则表达式必知必会 》这本书

8.2.1 . 一般字符的匹配

正则表达式是一种按照某种正则模式,从左到右匹配字符串中内容的一种工具。对于一般的字符而言,它可以找到其所在的位置,这里为了演示便利,使用了 python 中 re 模块的 findall 函数来匹配所有出现过但不重叠的模式,第一个参数是正则表达式,第二个参数是待匹配的字符串。例如,在下面的字符串中找出 apple :

import re

re.findall(r'Apple', 'Apple! This Is an Apple!')
Out[29]: ['Apple', 'Apple']

8.2.2 元字符基础

元字符描述.匹配除换行符以外的任意字符[ ]字符类,匹配方括号中包含的任意字符[^ ]否定字符类,匹配方括号中不包含的任意字符*匹配前面的子表达式零次或多次+匹配前面的子表达式一次或多次。比如r’d+’就是匹配数字串,r’d’就是匹配单个数字?匹配前面的子表达式零次或一次,非贪婪方式{n,m}花括号,匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式(xyz)字符组,按照确切的顺序匹配字符xyz|分支结构,匹配符号之前的字符或后面的字符\转义符,它可以还原元字符原来的含义^匹配行的开始$匹配行的结束

import re
re.findall(r'.', 'abc')
Out[30]: ['a', 'b', 'c']

re.findall(r'[ac]', 'abc')
Out[31]: ['a', 'c']

re.findall(r'[^ac]', 'abc')
Out[32]: ['b']

re.findall(r'[ab]{2}', 'aaaabbbb')
Out[33]: ['aa', 'aa', 'bb', 'bb']

re.findall(r'aaa|bbc|ca', 'aacabbcbbc')
Out[34]: ['ca', 'bbc', 'bbc']

"""
1. ?匹配的是前一个字符,即被转义的\,所以|前面的内容就是匹配a\或者a,但是结果里面没有a\,相当于只能匹配a。
2. |右边是a\*,转义之后匹配a*,对于竖线而言左边优先级高于右边
3. 然后看目标字符串aa?a*a,第一个a匹配左边,第二个a匹配左边,第三个a虽然后面有*,
但是左边优先级高, 还是匹配左边,剩下一个a还是左边,所以结果是四个a
"""

re.findall(r'a\\?|a\*', 'aa?a*a')
Out[35]: ['a', 'a', 'a', 'a']

re.findall(r'\\', 'aa\a*a')
[]

re.findall(r'a?.', 'abaacadaae')
Out[36]: ['ab', 'aa', 'c', 'ad', 'aa', 'e']

re.findall(r'(\w+)=(\d+)', 'set width=20 and height=10')
[('width', '20'), ('height', '10')]

8.2.3 简写字符集

则表达式中还有一类简写字符集,其等价于一组字符的集合:

简写描述\w匹配所有字母、数字、下划线: [a-zA-Z0-9_]\W匹配非字母和数字的字符: [^\w]\d匹配数字: [0-9]\D匹配非数字: [^\d]\s匹配空格符: [\t\n\f\r\p{Z}]\S匹配非空格符: [^\s]\B匹配非单词边界。’er\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’。

re.findall(r'.s', 'Apple! This Is an Apple!')
Out[37]: ['is', 'Is']

re.findall(r'\w{2}', '09 8? 7w c_ 9q p@')
Out[38]: ['09', '7w', 'c_', '9q']

re.findall(r'\w\W\B', '09 8? 7w c_ 9q p@')
Out[39]: ['8?', 'p@']

re.findall(r'.\s.', 'Constant dropping wears the stone.')
Out[40]: ['t d', 'g w', 's t', 'e s']

re.findall(r'上海市(.{2,3}区)(.{2,3}路)(\d+号)',
           '上海市黄浦区方浜中路249号 上海市宝山区密山路5号')

Out[41]: [('黄浦区', '方浜中路', '249号'), ('宝山区', '密山路', '5号')]

8.3 文本处理的五类操作

8.3.1 str.split 拆分

str.split能够把字符串的列进行拆分,其中第一个参数为正则表达式,可选参数包括从左到右的最大拆分次数 n ,是否展开为多个列 expand 。

s = pd.Series(['上海市黄浦区方浜中路249号',
            '上海市宝山区密山路5号'])

s.str.split('[市区路]')
Out[43]:
0    [上海, 黄浦, 方浜中, 249号]
1       [上海, 宝山, 密山, 5号]
dtype: object

s.str.split('[市区路]', n=2, expand=True)
Out[44]:
    0   1         2
0  上海  黄浦  方浜中路249号
1  上海  宝山     密山路5号

类似的函数是 str.rsplit ,其区别在于使用 n 参数的时候是从右到左限制最大拆分次数。但是当前版本下 rsplit 因为 bug 而无法使用正则表达式进行分割:

s.str.rsplit('[市区路]', n=2, expand=True)
Out[45]:
                0
0  上海市黄浦区方浜中路249号
1     上海市宝山区密山路5号

8.3.2 str.joinstr.cat 合并

  • str.join 表示用某个连接符把 Series 中的字符串列表连接起来,如果列表中出现了非字符串元素则返回缺失值。
  • str.cat 用于合并两个序列,主要参数为:
  • sep:连接符、
  • join:连接形式默认为以索引为键的左连接
  • na_rep:缺失值替代符号
s = pd.Series([['a','b'], [1, 'a'], [['a', 'b'], 'c']])
s.str.join('-')
Out[47]:
0    a-b
1    NaN
2    NaN
dtype: object
s1 = pd.Series(['a','b'])
s2 = pd.Series(['cat','dog'])
s1.str.cat(s2,sep='-')
Out[50]:
0    a-cat
1    b-dog
dtype: object

s2.index = [1, 2]
s1.str.cat(s2, sep='-', na_rep='?', join='outer')
Out[52]:
0      a-?

1    b-cat
2    ?-dog
dtype: object

8.3.3 匹配

  1. str.contains返回了每个字符串是否包含正则模式的布尔序列:
s = pd.Series(['my cat', 'he is fat', 'railway station'])
s.str.contains('\s\wat')

0     True
1     True
2    False
dtype: bool
  1. str.startswithstr.endswith返回了每个字符串以给定模式为开始和结束的布尔序列,它们都不支持正则表达式:
s.str.startswith('my')

0     True
1    False
2    False
dtype: bool
s.str.endswith('t')

0     True
1     True
2    False
dtype: bool
  1. str.match可以用正则表达式来检测开始或结束字符串的模式,其返回了每个字符串起始处是否符合给定正则模式的布尔序列。当然,这些也能通过在 str.contains的正则中使用 ^$来实现。(貌似没有python里的search方法)
s.str.match('m|h')
s.str.contains('^[m|h]')

0     True
1     True
2    False
dtype: bool
s.str[::-1].str.match('ta[f|g]|n')
s.str.contains('[f|g]at|n$')

0    False
1     True
2     True
dtype: bool
  1. str.findstr.rfind返回索引的匹配函数,其分别返回从左到右和从右到左第一次匹配的位置的索引,未找到则返回-1。需要注意的是这两个函数不支持正则匹配,只能用于字符子串的匹配:
s = pd.Series(['This is an apple. That is not an apple.'])

s.str.find('apple')
Out[62]:
0    11
dtype: int64

s.str.rfind('apple')
Out[63]:
0    33
dtype: int64
  1. 替换
    str.replacereplace并不是一个函数,在使用字符串替换时应当使用前者。
s = pd.Series(['a_1_b','c_?'])

s.str.replace('\d|\?', 'new', regex=True)

0    a_new_b
1      c_new
dtype: object

当需要对不同部分进行有差别的替换时,可以利用 &#x5B50;&#x7EC4;的方法,并且此时可以通过传入自定义的替换函数来分别进行处理,注意 group(k)代表匹配到的第 k个子组(圆括号之间的内容):

s = pd.Series(['上海市黄浦区方浜中路249号',
                '上海市宝山区密山路5号',
                '北京市昌平区北农路2号'])
pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
city = {'上海市': 'Shanghai', '北京市': 'Beijing'}
district = {'昌平区': 'CP District',
            '黄浦区': 'HP District',
            '宝山区': 'BS District'}
road = {'方浜中路': 'Mid Fangbin Road',
        '密山路': 'Mishan Road',
        '北农路': 'Beinong Road'}
def my_func(m):
    str_city = city[m.group(1)]
    str_district = district[m.group(2)]
    str_road = road[m.group(3)]
    str_no = 'No. ' + m.group(4)[:-1]
    return ' '.join([str_city,
                     str_district,
                     str_road,
                     str_no])
s.str.replace(pat, my_func, regex=True)
0    Shanghai HP District Mid Fangbin Road No. 249
1           Shanghai BS District Mishan Road No. 5
2           Beijing CP District Beinong Road No. 2
dtype: object

这里的数字标识并不直观,可以使用 &#x547D;&#x540D;&#x5B50;&#x7EC4;更加清晰地写出子组代表的含义:


pat = '(?P\w+市)(?P\w+区)(?P\w+路)(?P\d+号)'
def my_func(m):
    str_city = city[m.group('市名')]
    str_district = district[m.group('区名')]
    str_road = road[m.group('路名')]
    str_no = 'No. ' + m.group('编号')[:-1]
    return ' '.join([str_city,
                     str_district,
                     str_road,
                     str_no])
s.str.replace(pat, my_func, regex=True)
0    Shanghai HP District Mid Fangbin Road No. 249
1           Shanghai BS District Mishan Road No. 5
2           Beijing CP District Beinong Road No. 2
dtype: object

这里虽然看起来有些繁杂,但是实际数据处理中对应的替换,一般都会通过代码来获取数据从而构造字典映射,在具体写法上会简洁的多。

8.3.5 提取

  1. str.extract进行提取:提取既可以认为是一种返回具体元素值(而不是布尔值或元素对应的索引位置)的匹配操作,也可以认为是一种特殊的拆分操作。前面提到的 str.split例子中会把分隔符去除,这并不是用户想要的效果,这时候就可以用 str.extract进行提取:
s.str.split('[市区路]')
Out[43]:
0    [上海, 黄浦, 方浜中, 249号]
1       [上海, 宝山, 密山, 5号]
dtype: object

pat = '(\w+市)(\w+区)(\w+路)(\d+号)'
s.str.extract(pat)
Out[78]:
    0    1     2     3
0  上海市  黄浦区  方浜中路  249号
1  上海市  宝山区   密山路    5号
2  北京市  昌平区   北农路    2号

通过子组的命名,可以直接对新生成 DataFrame的列命名:

pat = '(?P\w+市)(?P\w+区)(?P\w+路)(?P\d+号)'
s.str.extract(pat)
Out[79]:
    市名   区名    路名    编号
0  上海市  黄浦区  方浜中路  249号
1  上海市  宝山区   密山路    5号
2  北京市  昌平区   北农路    2号
  1. str.extractall:不同于 str.extract只匹配一次,它会把所有符合条件的模式全部匹配出来,如果存在多个结果,则以多级索引的方式存储:
s = pd.Series(['A135T15,A26S5','B674S2,B25T6'], index = ['my_A','my_B'])
pat = '[A|B](\d+)[T|S](\d+)'
s.str.extractall(pat)
Out[83]:
       0   1
     match
my_A 0      135  15
     1       26   5
my_B 0      674   2
     1       25   6
pat_with_name = '[A|B](?P\d+)[T|S](?P\d+)'
s.str.extractall(pat_with_name)
Out[84]:
           name1 name2
     match
my_A 0       135    15
     1        26     5
my_B 0       674     2
     1        25     6
  1. str.findall:功能类似于 str.extractall,区别在于前者把结果存入列表中,而后者处理为多级索引,每个行只对应一组匹配,而不是把所有匹配组合构成列表。
s.str.findall(pat)
my_A    [(135, 15), (26, 5)]
my_B     [(674, 2), (25, 6)]
dtype: object

8.4、常用字符串函数

除了上述介绍的五类字符串操作有关的函数之外, str对象上还定义了一些实用的其他方法,在此进行介绍。

8.4.1 字母型函数

upper, lower, title, capitalize, swapcase这五个函数主要用于字母的大小写转化,从下面的例子中就容易领会其功能:

s = pd.Series(['lower', 'CAPITALS', 'this is a sentence', 'SwApCaSe'])

s.str.upper()
Out[87]:
0                 LOWER
1              CAPITALS
2    THIS IS A SENTENCE
3              SWAPCASE
dtype: object

s.str.lower()
Out[88]:
0                 lower
1              capitals
2    this is a sentence
3              swapcase
dtype: object

s.str.title()
Out[89]:
0                 Lower
1              Capitals
2    This Is A Sentence
3              Swapcase
dtype: object

s.str.capitalize()
Out[90]:
0                 Lower
1              Capitals
2    This is a sentence
3              Swapcase
dtype: object

s.str.swapcase()
Out[91]:
0                 LOWER
1              capitals
2    THIS IS A SENTENCE
3              sWaPcAsE
dtype: object

s.str.casefold()

0                 lower
1              capitals
2    this is a sentence
3              swapcase

8.4.2 数值型函数

这里着重需要介绍的是 pd.to_numeric方法,它虽然不是 str对象上的方法,但是能够对字符格式的数值进行快速转换和筛选。其主要参数包括:

  • errors:非数值的处理模式。对于不能转换为数值的有三种 errors选项:
  • raise:直接报错,默认选项
  • coerce:设为缺失值
  • ignore:保持原来的字符串。
  • downcast:转换类型,转成 ‘integer’, ‘signed’, ‘unsigned’, 或 ‘float’的最小dtype。比如可以转成float32就不会转成float64。
s = pd.Series(['1', '2.2', '2e', '??', '-2.1', '0'])

pd.to_numeric(s, errors='ignore')
Out[93]:
0       1
1     2.2
2      2e
3      ??

4    -2.1
5       0
dtype: object

pd.to_numeric(s, errors='coerce')
Out[94]:
0    1.0
1    2.2
2    NaN
3    NaN
4   -2.1
5    0.0
dtype: float64

在数据清洗时,可以利用 coerce的设定,快速查看非数值型的行:

s[pd.to_numeric(s, errors='coerce').isna()]
Out[95]:
2    2e
3    ??

dtype: object

8.4.3 统计型函数

countlen的作用分别是返回出现正则模式的次数和字符串的长度:

s = pd.Series(['cat rat fat at', 'get feed sheet heat'])

s.str.count('[r|f]at|ee')
Out[97]:
0    2
1    2
dtype: int64

s.str.len()
Out[98]:
0    14
1    19
dtype: int64

8.4.4 格式型函数

格式型函数主要分为两类,第一种是除空型,第二种是填充型。其中,第一类函数一共有三种,它们分别是 strip, rstrip, lstrip,分别代表去除两侧空格、右侧空格和左侧空格。这些函数在数据清洗时是有用的,特别是列名含有非法空格的时候。

my_index = pd.Index([' col1', 'col2 ', ' col3 '])

my_index.str.strip().str.len()
Out[100]: Int64Index([4, 4, 4], dtype='int64')

my_index.str.rstrip().str.len()
Out[101]: Int64Index([5, 4, 5], dtype='int64')

my_index.str.lstrip().str.len()
Out[102]: Int64Index([4, 5, 5], dtype='int64')

对于填充型函数而言, pad是最灵活的,它可以选定字符串长度、填充的方向和填充内容:

s = pd.Series(['a','b','c'])

s.str.pad(5,'left','*')
Out[104]:
0    ****a
1    ****b
2    ****c
dtype: object

s.str.pad(5,'right','*')
Out[105]:
0    a****
1    b****
2    c****
dtype: object

s.str.pad(5,'both','*')
Out[106]:
0    **a**
1    **b**
2    **c**
dtype: object

上述的三种情况可以分别用 rjust, ljust, center来等效完成,需要注意 ljust是指右侧填充而不是左侧填充:

s.str.rjust(5, '*')
Out[107]:
0    ****a
1    ****b
2    ****c
dtype: object

s.str.ljust(5, '*')
Out[108]:
0    a****
1    b****
2    c****
dtype: object

s.str.center(5, '*')
Out[109]:
0    **a**
1    **b**
2    **c**
dtype: object

在读取 excel文件时,经常会出现数字前补0的需求,例如证券代码读入的时候会把”000007″作为数值7来处理, pandas中除了可以使用上面的左侧填充函数进行操作之外,还可用 zfill来实现。

s = pd.Series([7, 155, 303000]).astype('string')

s.str.pad(6,'left','0')
Out[111]:
0    000007
1    000155
2    303000
dtype: string

s.str.rjust(6,'0')
Out[112]:
0    000007
1    000155
2    303000
dtype: string

s.str.zfill(6)
Out[113]:
0    000007
1    000155
2    303000
dtype: string

8.5 练习

Ex1:房屋信息数据集

现有一份房屋信息数据集如下:

df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
df.head(3)
Out[115]:
      floor    year    area price
0   高层(共6层)  1986年建  58.23㎡  155万
1  中层(共20层)  2020年建     88㎡  155万
2  低层(共28层)  2010年建  89.33㎡  365万
  • 将year列改为整数年份存储。
  • 将floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
  • 计算房屋每平米的均价avg_price,以元/平米的格式存储到表中,其中为整数

  • year列改为整数年份存储。

"""
整个序列需要先转成Nullable类型的String类型,取出年份,再将年份转为Int64类型。
注意,转换的类型是Int64不是int,否则报错。即使astype加参数errors='ignore'跳过缺失值,
转成int后,序列还有缺失值所以,还是变成了object。
而整个序列转为Int,就还是Int类型,缺失值变成了 pd.NA 。
"""
df = df.convert_dtypes()
df['year']=df['year'].str.replace('\D','',regex=True).astype('Int64')
df.loc[df.year.notna()]['year'].head()

0        1986
1        2020
2        2010
3        2014
4        2015
Name: year, Length: 12850, dtype: Int64

参考答案:

不知道为啥 pd.to_numeric(df.year.str[:-2],downcast="integer")类型为float32,不应该是整型么

df.year = pd.to_numeric(df.year.str[:-2]).astype('Int64')
df.loc[df.year.notna()]['year']
  1. floor列替换为 Level, Highest两列,其中的元素分别为 string类型的层类别(高层、中层、低层)与整数类型的最高层数。
pat = '(?P\w+层)(?P\(\w+层)'
df2=df['floor'].str.extract(pat)
df=pd.concat([df,df2],axis=1).convert_dtypes()
df['Highest']=df['Highest'].str.replace('\D+','',regex=True).astype('Int64')
df=df[['Level','Highest','year','area','price']]
df.head()

   Level  Highest   year    area    price
0   高层      6       1986    58.23㎡  155万
1   中层      20      2020    88㎡ 155万
2   低层      28      2010    89.33㎡  365万
3   低层      20      2014    82㎡ 308万
4   高层      1       2015    98㎡ 117万

pat = '(\w层)(共(\d+)层)'
new_cols = df.floor.str.extract(pat).rename(
                    columns={0:'Level', 1:'Highest'})

df = pd.concat([df.drop(columns=['floor']), new_cols], 1)
df.head(3)

Out[163]:
   year    area price    Level Highest
0  1986  58.23㎡  155万    高层       6
1  2020     88㎡  155万    中层      20
2  2010  89.33㎡  365万    低层      28
  1. 计算房屋每平米的均价 avg_price,以 ***&#x5143;/&#x5E73;&#x7C73;的格式存储到表中,其中 ***为整数。
"""
str.findall返回的结果都是列表,只能用apply取值去掉列表形式
参考答案用pd.to_numeric(df.area.str[:-1])更简洁
由于area和price都没有缺失值,所以可以直接转类型
"""
df['new_area']=df['area'].str.findall(r'\d+.\d+|\d+').apply(lambda x:float(x[0]))
df['new_price']=df['price'].str.replace('\D+','',regex=True).astype('int64')
df.eval('avg_price=10000*new_price/new_area',inplace=True)

df['avg_price']=df['avg_price'].astype('int').astype('string')+'元/平米'
del df['new_area'],df['new_price']
df.head()

   Level    Highest year    area    price   avg_price
0   高层          6   1986    58.23㎡  155万    26618元/平米
1   中层          20  2020    88㎡ 155万    17613元/平米
2   低层          28  2010    89.33㎡  365万    40859元/平米
3   低层          20  2014    82㎡ 308万    37560元/平米
4   高层          1   2015    98㎡ 117万    11938元/平米

s_area = pd.to_numeric(df.area.str[:-1])
s_price = pd.to_numeric(df.price.str[:-1])
df['avg_price'] = ((s_price/s_area)*10000).astype(
                    'int').astype('string') + '元/平米'

df.head(3)
Out[167]:
   year    area   price   Level Highest  avg_price
0  1986  58.23㎡  155万    高层     6   26618元/平米
1  2020     88㎡  155万    中层     20      17613元/平米
2  2010  89.33㎡  365万    低层     28      40859元/平米

Ex2:《权力的游戏》剧本数据集

现有一份权力的游戏剧本数据集如下:

df = pd.read_csv('../data/script.csv')
df.head(3)

Out[115]:
Out[117]:
  Release Date    Season   Episode      Episode Title          Name                                           Sentence
0   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce  What do you expect? They're savages. One lot s...

1   2011-04-17  Season 1  Episode 1  Winter is Coming          will  I've never seen wildlings do a thing like this...

2   2011-04-17  Season 1  Episode 1  Winter is Coming  waymar royce
  • 计算每一个Episode的台词条数。
  • 以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
  • 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 𝑛 个问号,则认为回答者回答了 𝑛 个问题,请求出回答最多问题的前五个人。

  • 计算每一个 Episode的台词条数。

df.columns =df.columns.str.strip()
df.groupby(['Season','Episode'])['Sentence'].count().sort_values(ascending=False).head()

season    Episode
Season 7  Episode 5    505
Season 3  Episode 2    480
Season 4  Episode 1    475
Season 3  Episode 5    440
Season 2  Episode 2    432
  1. 以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。

df['len_words']=df['Sentence'].str.count(r' ')+1
df.groupby(['Name'])['len_words'].mean().sort_values(ascending=False).head()

Name
male singer          109.000000
slave owner           77.000000
manderly              62.000000
lollys stokeworth     62.000000
dothraki matron       56.666667
Name: len_words, dtype: float64
  1. 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有n n n个问号,则认为回答者回答了n n n个问题,请求出回答最多问题的前五个人。
df['Sentence'].str.count(r'\?')
ls=pd.concat([pd.Series(0),ls]).reset_index(drop=True)
del ls[23911]
df['len_questions']=ls
df.groupby(['Name'])['len_questions'].sum().sort_values(ascending=False).head()

Name
tyrion lannister    527
jon snow            374
jaime lannister     283
arya stark          265
cersei lannister    246
Name: len_questions, dtype: int64

s = pd.Series(df.Sentence.values, index=df.Name.shift(-1))
s.str.count('\?').groupby('Name').sum().sort_values(ascending=False).head()

第九章 分类数据

import numpy as np
import pandas as pd

9.1 cat对象

9.1.1 cat对象的属性

pandas中提供了 category类型,使用户能够处理分类类型的变量,将一个普通序列转换成分类变量可以使用 astype方法。

df = pd.read_csv('data/learn_pandas.csv',
     usecols = ['Grade', 'Name', 'Gender', 'Height', 'Weight'])
s = df.Grade.astype('category')

s.head()
Out[5]:
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman', 'Junior', 'Senior', 'Sophomore']

在一个分类类型的 Series中定义了 cat对象,它和上一章中介绍的 str对象类似,定义了一些属性和方法来进行分类类别的操作。

s.cat
Out[6]: <pandas.core.arrays.categorical.CategoricalAccessor object at 0x000002B7974C20A0>

cat的属性:

  • cat.categories:查看类别的本身,它以 Index类型存储
  • cat.ordered:类别是否有序
  • cat.codes:访问类别编号。每一个序列的类别会被赋予唯一的整数编号,它们的编号取决于 cat.categories中的顺序
s.cat.categories
Out[7]: Index(['Freshman', 'Junior', 'Senior', 'Sophomore'], dtype='object')

s.cat.ordered
Out[8]: False

s.cat.codes.head()
Out[9]:
0    0
1    0
2    2
3    3
4    3
dtype: int8

9.1.2 类别的增加、删除和修改

通过 cat对象的 categories属性能够完成对类别的查询,那么应该如何进行”增改查删”的其他三个操作呢?

【NOTE】类别不得直接修改
在第三章中曾提到,索引 Index 类型是无法用 index_obj[0] = item 来修改的,而 categories 被存储在 Index 中,因此 pandascat 属性上定义了若干方法来达到相同的目的。

  1. add_categories:增加类别
s = s.cat.add_categories('Graduate')
s.cat.categories

Index(['Freshman', 'Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')
  1. remove_categories:删除类别。同时所有原来序列中的该类会被设置为缺失。
s = s.cat.remove_categories('Freshman')

s.cat.categories
Out[13]: Index(['Junior', 'Senior', 'Sophomore', 'Graduate'], dtype='object')

s.head()
Out[14]:
0          NaN
1          NaN
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Junior', 'Senior', 'Sophomore', 'Graduate']
  1. set_categories:直接设置序列的新类别,原来的类别中如果存在元素不属于新类别,那么会被设置为缺失。相当于索引重设。
s = s.cat.set_categories(['Sophomore','PhD'])
s.cat.categories
Out[16]: Index(['Sophomore', 'PhD'], dtype='object')

s.head()
Out[17]:
0          NaN
1          NaN
2          NaN
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (2, object): ['Sophomore', 'PhD']
  1. remove_unused_categories:删除未出现在序列中的类别
s = s.cat.remove_unused_categories()
s.cat.categories

Index(['Sophomore'], dtype='object')
  1. rename_categories:修改序列的类别。注意,这个方法会对原序列的对应值也进行相应修改。例如,现在把 Sophomore改成中文的 &#x672C;&#x79D1;&#x4E8C;&#x5E74;&#x7EA7;&#x5B66;&#x751F;
s = s.cat.rename_categories({'Sophomore':'本科二年级学生'})
s.head()

0        NaN
1        NaN
2        NaN
3    本科二年级学生
4    本科二年级学生
Name: Grade, dtype: category
Categories (1, object): ['本科二年级学生']

9.2 有序分类

9.2.1 序的建立

有序类别和无序类别可以通过 as_unorderedreorder_categories互相转化。 reorder_categories传入的参数必须是由当前序列的无序类别构成的列表,不能够新增或减少原先的类别,且必须指定参数 ordered=True,否则方法无效。例如,对年级高低进行相对大小的类别划分,然后再恢复无序状态:

s = df.Grade.astype('category')
s = s.cat.reorder_categories(['Freshman', 'Sophomore',
                              'Junior', 'Senior'],ordered=True)
s.head()
Out[24]:
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman' < 'Sophomore' < 'Junior' < 'Senior']

s.cat.as_unordered().head()
Out[25]:
0     Freshman
1     Freshman
2       Senior
3    Sophomore
4    Sophomore
Name: Grade, dtype: category
Categories (4, object): ['Freshman', 'Sophomore', 'Junior', 'Senior']

如果不想指定 ordered=True参数,那么可以先用 s.cat.as_ordered()转化为有序类别,再利用 reorder_categories进行具体的相对大小调整。

9.2.2 排序和比较

在第二章中,曾提到了字符串和数值类型序列的排序。前者按照字母顺序排序,后者按照数值大小排序。

分类变量排序,只需把列的类型修改为 category后,再赋予相应的大小关系,就能正常地使用 sort_indexsort_values。例如,对年级进行排序:

df.Grade = df.Grade.astype('category')
df.Grade = df.Grade.cat.reorder_categories(['Freshman', 'Sophomore', 'Junior', 'Senior'],ordered=True)
df.sort_values('Grade').head()
Out[28]:
        Grade           Name  Gender  Height  Weight
0    Freshman   Gaopeng Yang  Female   158.9    46.0
105  Freshman      Qiang Shi  Female   164.5    52.0
96   Freshman  Changmei Feng  Female   163.8    56.0
88   Freshman   Xiaopeng Han  Female   164.1    53.0
81   Freshman    Yanli Zhang  Female   165.1    52.0

df.set_index('Grade').sort_index().head()
Out[29]:
                   Name  Gender  Height  Weight
Grade
Freshman   Gaopeng Yang  Female   158.9    46.0
Freshman      Qiang Shi  Female   164.5    52.0
Freshman  Changmei Feng  Female   163.8    56.0
Freshman   Xiaopeng Han  Female   164.1    53.0
Freshman    Yanli Zhang  Female   165.1    52.0

由于序的建立,因此就可以进行比较操作,方便后续索引操作。分类变量的比较操作分为两类:

  1. ==!=关系的比较,比较的对象可以是标量或者同长度的 Series(或 list)。(无序时也可以比较)
  2. >,>=,<,<=< code>&#x56DB;&#x7C7B;&#x5927;&#x5C0F;&#x5173;&#x7CFB;&#x7684;&#x6BD4;&#x8F83;&#xFF0C;&#x6BD4;&#x8F83;&#x7684;&#x5BF9;&#x8C61;&#x548C;&#x7B2C;&#x4E00;&#x79CD;&#x7C7B;&#x4F3C;&#xFF0C;&#x4F46;&#x662F;&#x6240;&#x6709;&#x53C2;&#x4E0E;&#x6BD4;&#x8F83;&#x7684;&#x5143;&#x7D20;&#x5FC5;&#x987B;&#x5C5E;&#x4E8E;&#x539F;&#x5E8F;&#x5217;&#x7684;<code>categories</code>&#xFF0C;&#x540C;&#x65F6;&#x8981;&#x548C;&#x539F;&#x5E8F;&#x5217;&#x5177;&#x6709;&#x76F8;&#x540C;&#x7684;&#x7D22;&#x5F15;&#x3002;<!--,<=<-->
res1 = df.Grade == 'Sophomore'

res1.head()
Out[31]:
0    False
1    False
2    False
3     True
4     True
Name: Grade, dtype: bool

res2 = df.Grade == ['PhD']*df.shape[0]

res2.head()
Out[33]:
0    False
1    False
2    False
3    False
4    False
Name: Grade, dtype: bool

res3 = df.Grade  'Sophomore'

res3.head()
Out[35]:
0     True
1     True
2    False
3     True
4     True
Name: Grade, dtype: bool

res4 = df.Grade  df.Grade.sample(frac=1).reset_index(drop=True)

res4.head()
Out[37]:
0     True
1     True
2    False
3     True
4     True
Name: Grade, dtype: bool

9.3 区间类别

9.3.1 利用cut和qcut进行区间构造

区间是一种特殊的类别,在实际数据分析中,区间序列往往是通过 cutqcut方法进行构造的,这两个函数能够把原序列的数值特征进行装箱,即用区间位置来代替原来的具体数值。

  1. cut函数常用参数有:

  2. bins:最重要的参数。

  3. 如果传入整数 n,则表示把整个传入数组按照最大和最小值等间距地分为 n段。默认 right=True,即区间是左开右闭,需要在调整时把最小值包含进去。(在 pandas中的解决方案是在值最小的区间左端点再减去 0.001*(max-min)。)
  4. 也可以传入列表,表示按指定区间分割点分割。

如果对序列 [1,2]划分为2个箱子时,第一个箱子的范围 (0.999,1.5],第二个箱子的范围是 (1.5,2]
如果需要指定区间为左闭右开,需要把 right参数设置为 False,相应的区间调整方法是在值最大的区间右端点再加上 0.001*(max-min)

s = pd.Series([1,2])

pd.cut(s, bins=2)
Out[39]:
0    (0.999, 1.5]
1      (1.5, 2.0]
dtype: category
Categories (2, interval[float64]): [(0.999, 1.5] < (1.5, 2.0]]

pd.cut(s, bins=2, right=False)
Out[40]:
0      [1.0, 1.5)
1    [1.5, 2.001)
dtype: category
Categories (2, interval[float64]): [[1.0, 1.5) < [1.5, 2.001)]

pd.cut(s, bins=[-np.infty, 1.2, 1.8, 2.2, np.infty])
Out[41]:
0    (-inf, 1.2]
1     (1.8, 2.2]
dtype: category
Categories (4, interval[float64]): [(-inf, 1.2] < (1.2, 1.8] < (1.8, 2.2] < (2.2, inf]]
  • labels:区间的名字
  • retbins:是否返回分割点(默认不返回)

默认 retbins=Flase时,返回每个元素所属区间的列表
retbins=True时,返回的是元组,两个元素分别是元素所属区间和分割点。所属区间可再次用索引取值

s = df.Weight
res = pd.cut(s, bins=3, labels=['small', 'mid','big'],retbins=True)
res[0][:2]

Out[44]:
0    small
1      big
dtype: category
Categories (2, object): ['small' < 'big']

res[1]
Out[45]: array([0.999, 1.5  , 2.   ])
  1. qcut函数。其用法 cut几乎没有差别,只是把 bins参数变成 q参数( quantile)。
  2. q为整数 n时,指按照 n等分位数把数据分箱
  3. q为浮点列表时,表示相应的分位数分割点。
s = df.Weight

pd.qcut(s, q=3).head()
Out[47]:
0    (33.999, 48.0]
1      (55.0, 89.0]
2      (55.0, 89.0]
3    (33.999, 48.0]
4      (55.0, 89.0]
Name: Weight, dtype: category
Categories (3, interval[float64]): [(33.999, 48.0] < (48.0, 55.0] < (55.0, 89.0]]

pd.qcut(s, q=[0,0.2,0.8,1]).head()
Out[48]:
0      (44.0, 69.4]
1      (69.4, 89.0]
2      (69.4, 89.0]
3    (33.999, 44.0]
4      (69.4, 89.0]
Name: Weight, dtype: category
Categories (3, interval[float64]): [(33.999, 44.0] < (44.0, 69.4] < (69.4, 89.0]]

9.3.2 一般区间的构造

pandas的单个区间用 Interval表示,对于某一个具体的区间而言,其具备三个要素,即左端点、右端点和端点的开闭状态。

  1. 开闭状态:包含四种,即 right&#xFF08;&#x5DE6;&#x5F00;&#x53F3;&#x95ED;&#xFF09;, left&#xFF08;&#x5DE6;&#x95ED;&#x53F3;&#x5F00;&#xFF09;, both&#xFF08;&#x4E24;&#x8FB9;&#x90FD;&#x95ED;&#xFF09;, neither&#xFF08;&#x4E24;&#x8FB9;&#x90FD;&#x5F00;&#xFF09;
my_interval = pd.Interval(0, 1, 'right')

my_interval
Out[50]: Interval(0, 1, closed='right')
  1. 区间属性:包含 left,mid,right,length,closed,,分别表示左中右端点、长度和开闭状态。
  2. 使用 in可以判断元素是否属于区间
  3. overlaps可以判断两个区间是否有交集:
0.5 in my_interval

True
my_interval_2 = pd.Interval(0.5, 1.5, 'left')
my_interval.overlaps(my_interval_2)

True

pd.IntervalIndex对象有四类方法生成,分别是 from_breaks, from_arrays, from_tuples, interval_range,它们分别应用于不同的情况:

  1. from_breaks:类似于 cutqcut函数,只不过后两个是通过计算得到的分割点,而前者是直接传入自定义的分割点:
pd.IntervalIndex.from_breaks([1,3,6,10], closed='both')

IntervalIndex([[1, 3], [3, 6], [6, 10]],
               closed='both',
               dtype='interval[int64]')
  1. from_arrays:分别传入左端点和右端点的列表,适用于 有交集并且知道起点和终点的情况:
pd.IntervalIndex.from_arrays(left = [1,3,6,10], right = [5,4,9,11], closed = 'neither')

IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
                  closed='neither',
                  dtype='interval[int64]')
  1. from_tuples:传入起点和终点元组构成的列表:
pd.IntervalIndex.from_tuples([(1,5),(3,4),(6,9),(10,11)], closed='neither')

IntervalIndex([(1, 5), (3, 4), (6, 9), (10, 11)],
              closed='neither',
              dtype='interval[int64]')
  1. interval_range:生成等差区间。其参数有四个: start, end, periods, freq。分别表示等差区间的起点、终点、区间个数和区间长度。其中三个量确定的情况下,剩下一个量就确定了,从而就能构造出相应的区间:
pd.interval_range(start=1,end=5,periods=8)
Out[57]:
IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
              closed='right',
              dtype='interval[float64]')

pd.interval_range(end=5,periods=8,freq=0.5)
Out[58]:
IntervalIndex([(1.0, 1.5], (1.5, 2.0], (2.0, 2.5], (2.5, 3.0], (3.0, 3.5], (3.5, 4.0], (4.0, 4.5], (4.5, 5.0]],
              closed='right',
              dtype='interval[float64]')

【练一练】
无论是 interval_range还是下一章时间序列中的 date_range都是给定了等差序列中四要素中的三个,从而确定整个序列。请回顾等差数列中的首项、末项、项数和公差的联系,写出 interval_range中四个参数之间的恒等关系。

除此之外,如果直接使用 pd.IntervalIndex([...], closed=...),把 Interval类型的列表组成传入其中转为区间索引,那么 所有的区间会被强制转为指定的 closed 类型,因为 pd.IntervalIndex只允许存放同一种开闭区间的 Interval对象。

my_interval
Out[59]: Interval(0, 1, closed='right')

my_interval_2
Out[60]: Interval(0.5, 1.5, closed='left')

pd.IntervalIndex([my_interval, my_interval_2], closed='left')
Out[61]:
IntervalIndex([[0.0, 1.0), [0.5, 1.5)],
              closed='left',
              dtype='interval[float64]')

9.3.3 区间的属性与方法

IntervalIndex上也定义了一些有用的属性和方法。同时,如果想要具体利用 cut或者 qcut的结果进行分析,那么需要先将其转为该种索引类型:

s=df.Weight
id_interval = pd.IntervalIndex(pd.cut(s, 3))
id_interval[:3]

IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0]],
                 closed='right',
                 name='Weight',
                 dtype='interval[float64]')
  1. 与单个 Interval类型相似, IntervalIndex有若干常用属性: left, right, mid, length,分别表示左右端点、两 点均值和区间长度。
id_demo = id_interval[:5]

id_demo
Out[64]:
IntervalIndex([(33.945, 52.333], (52.333, 70.667], (70.667, 89.0], (33.945, 52.333], (70.667, 89.0]],
              closed='right',
              name='Weight',
              dtype='interval[float64]')

id_demo.left
Out[65]: Float64Index([33.945, 52.333, 70.667, 33.945, 70.667], dtype='float64')

id_demo.right
Out[66]: Float64Index([52.333, 70.667, 89.0, 52.333, 89.0], dtype='float64')

id_demo.mid
Out[67]: Float64Index([43.138999999999996, 61.5, 79.8335, 43.138999999999996, 79.8335], dtype='float64')

id_demo.length
Out[68]:
Float64Index([18.387999999999998, 18.334000000000003, 18.333,
              18.387999999999998, 18.333],
             dtype='float64')
  1. IntervalIndex还有两个常用方法:
  2. contains:逐个判断每个区间是否包含某元素
  3. overlaps:是否和一个 pd.Interval对象有交集。
id_demo.contains(50)
Out[69]: array([ True, False, False,  True, False])

id_demo.overlaps(pd.Interval(40,60))
Out[70]: array([ True,  True, False,  True, False])

9.4 练习

Ex1: 统计未出现的类别

在第五章中介绍了 crosstab函数,在默认参数下它能够对两个列的组合出现的频数进行统计汇总:

df = pd.DataFrame({'A':['a','b','c','a'], 'B':['cat','cat','dog','cat']})
pd.crosstab(df.A, df.B)

Out[72]:
B  cat  dog
A
a    2    0
b    1    0
c    0    1

但事实上有些列存储的是分类变量,列中并不一定包含所有的类别,此时如果想要对这些未出现的类别在 crosstab结果中也进行汇总,则可以指定 dropna参数为 False

df.B = df.B.astype('category').cat.add_categories('sheep')
pd.crosstab(df.A, df.B, dropna=False)

Out[74]:
B  cat  dog  sheep
A
a    2    0      0
b    1    0      0
c    0    1      0

请实现一个带有 dropna参数的 my_crosstab函数来完成上面的功能。

Ex2: 钻石数据集

现有一份关于钻石的数据集,其中 carat, cut, clarity, price分别表示克拉重量、切割质量、纯净度和价格,样例如下:

df = pd.read_csv('../data/diamonds.csv')
df.head(3)

Out[76]:
   carat      cut    clarity  price
0   0.23     Ideal     SI2     326
1   0.21    Premium    SI1     326
2   0.23     Good      VS1     327
  1. 分别对 df.cutobject类型和 category类型下使用 nunique函数,并比较它们的性能。
  2. 钻石的切割质量可以分为五个等级,由次到好分别是 Fair, Good, Very Good, Premium, Ideal,纯净度有八个等级,由次到好分别是 I1, SI2, SI1, VS2, VS1, VVS2, VVS1, IF,请对切割质量按照 由好到次的顺序排序,相同切割质量的钻石,按照纯净度进行 由次到好的排序。
  3. 分别采用两种不同的方法,把 cut, clarity这两列按照 由好到次的顺序,映射到从0到n-1的整数,其中n表示类别的个数。
  4. 对每克拉的价格分别按照分位数(q=[0.2, 0.4, 0.6, 0.8])与[1000, 3500, 5500, 18000]割点进行分箱得到五个类别 Very Low, Low, Mid, High, Very High,并把按这两种分箱方法得到的 category序列依次添加到原表中。
  5. 第4问中按照整数分箱得到的序列中,是否出现了所有的类别?如果存在没有出现的类别请把该类别删除。
  6. 对第4问中按照分位数分箱得到的序列,求每个样本对应所在区间的左右端点值和长度。

先看看数据结构:

`python
df.info()
Data columns (total 4 columns):

Original: https://blog.csdn.net/qq_56591814/article/details/126633913
Author: 神洛华
Title: datawhale8月组队学习《pandas数据处理与分析》(下)(文本、分类、时序数据)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/694187/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球