pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充
本文部分引用了原教程,并参考了
另注:本文是对joyful pandas教程的延伸,完整理解需先阅读joyful pandas教程第八章
现有一份房屋信息数据集如下:
year
列改为整数年份存储。floor
列替换为Level, Highest
两列,其中的元素分别为string
类型的层类别(高层、中层、低层)与整数类型的最高层数。avg_price
,以***元/平米
的格式存储到表中,其中***
为整数。df = pd.read_excel('../data/house_info.xls', usecols=['floor','year','area','price'])
df.head()
floor | 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万 |
第一题和第二题都是通过正则挖出相应的内容即可
df.year = df.year.str.extract('(\d{1,4})')
df.head(2)
floor | year | area | price | |
---|---|---|---|---|
0 | 高层(共6层) | 1986 | 58.23㎡ | 155万 |
1 | 中层(共20层) | 2020 | 88㎡ | 155万 |
%timeit pd.to_numeric(df.year.str[:-2])
14.9 ms ± 1.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df.year.str.extract('(\d{1,4})')
29 ms ± 7.51 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
这里将我的做法和答案对比了一下,发现答案用to_nermeric还是比正则要快很多的
df[['Level', 'Highest']] = df.floor.str.extract('(高层|中层|低层)\(共(\d+)层\)')
df.head(2)
floor | year | area | price | Level | Highest | |
---|---|---|---|---|---|---|
0 | 高层(共6层) | 1986 | 58.23㎡ | 155万 | 高层 | 6 |
1 | 中层(共20层) | 2020 | 88㎡ | 155万 | 中层 | 20 |
先检查下price
列有没有浮点数
df[df.price.str.find('.')!=-1].head(2)
floor | year | area | price | Level | Highest | |
---|---|---|---|---|---|---|
14 | 高层(共6层) | 1998 | 65㎡ | 33.8万 | 高层 | 6 |
37 | 低层(共6层) | 2020 | 135.06㎡ | 142.8万 | 低层 | 6 |
果然!price列并不是整数,所以使用正则提取的时候要注意,price和area可以按照同样的方法提取,如下
price = df.price.str.extract('(\d+.?\d+)').astype('float')
area = df.area.str.extract('(\d+.?\d+)').astype('float')
(price/area*10000).isna().sum()
0 154
dtype: int64
df.price.isna().sum()
0
第一遍做出来的结果测试时发现不太对,结果里包含空值,而price
列和area
列是没空值的,所以再找下错误
df[df.price.str.extract('(\d+\.?\d+)')[0].isna()].head(2)
floor | year | area | price | Level | Highest | |
---|---|---|---|---|---|---|
371 | 高层(共1层) | NaN | 11.17㎡ | 7万 | 高层 | 1 |
624 | 低层(共6层) | NaN | 16.8㎡ | 6万 | 低层 | 6 |
可以看出错误出现在价格是个位数的情况,因为我的正则表达式在可选的句点两边都是至少有一个数字,当出现两个数字时就出错了,所以这里将后面的数字改为出现0次或多次
price = df.price.str.extract('(\d+.?\d*)万').astype('float')
area = df.area.str.extract('(\d+.?\d*)㎡').astype('float')
df['avg_price'] = np.round(price/area*10000).astype('int')
df['avg_price'] = df['avg_price'].apply(lambda x: str(x)+'元/平米')
df.head()
floor | year | area | price | Level | Highest | avg_price | |
---|---|---|---|---|---|---|---|
0 | 高层(共6层) | 1986 | 58.23㎡ | 155万 | 高层 | 6 | 26619元/平米 |
1 | 中层(共20层) | 2020 | 88㎡ | 155万 | 中层 | 20 | 17614元/平米 |
2 | 低层(共28层) | 2010 | 89.33㎡ | 365万 | 低层 | 28 | 40860元/平米 |
3 | 低层(共20层) | 2014 | 82㎡ | 308万 | 低层 | 20 | 37561元/平米 |
4 | 高层(共1层) | 2015 | 98㎡ | 117万 | 高层 | 1 | 11939元/平米 |
现有一份权力的游戏剧本数据集如下:
df = pd.read_csv('../data/script.csv')
df.head(3)
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 | How close did you get? |
Episode
的台词条数。【第一问】
这里因为分组好几次都出现了KeyError,所以查看下各列的名称,发现Season
和Epsode
列分别在前面和后面有空格,因此需要预处理一下
df.columns = df.columns.str.strip()
df.groupby(['Season', 'Episode'])['Sentence'].count().head(3)
Season Episode
Season 1 Episode 1 327
Episode 10 266
Episode 2 283
Name: Sentence, dtype: int64
【第二问】
求出的是每个人的台词,因此要按Name
分组,对每句话使用空格分割求数量
这里我先使用了以往的思路,对每句话split并求长度
现在学了正则后再用正则的方式匹配一句话中空格的数量+1其实就是单词的数量
a = df['Sentence'].apply(lambda x: len(x.split(' ')))
b = df['Sentence'].str.count(' ')+1
(a==b).all()
True
可以看出两个算出来结果是等价的,再测试下这两种写法的效率
%timeit df['Sentence'].apply(lambda x: len(x.split(' ')))
31.5 ms ± 4.43 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit df['Sentence'].str.count(' ')+1
32.9 ms ± 762 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Emmmmn…跟自己想的不太一样,总觉得正则每行遍历一遍应该很快啊,但这里用apply也挺快的,猜想可能是python对len和split这个组合有优化?测试了好几遍都是正则时间要多一些,不过拥有新武器永远是一件乐事。
再找一下最多的五个人
df['words'] = df['Sentence'].str.count(' ')+1
df.groupby('Name').agg('mean')['words'].sort_values()[-5:].index
Index(['dothraki matron', 'lollys stokeworth', 'manderly', 'slave owner',
'male singer'],
dtype='object', name='Name')
【第三问】
思路:1)先求出每个人的问号数量;2)将整个列整体下移匹配每个人回答问题的数量(这里记得教程里曾经讲过一个函数可以整体平移,于是又翻了翻教程,发现是滑窗类的shift);3)求和并求出人
df['answers'] = df['Sentence'].str.count('\?').shift(1)
df.groupby('Name')['answers'].agg('sum').sort_values()[-5:].index
Index(['cersei lannister', 'arya stark', 'jaime lannister', 'jon snow',
'tyrion lannister'],
dtype='object', name='Name')
对了下答案发现都没问题,因为这次学了正则所以都用正则的方法试着做了做,发现正则的速度还是比较慢的,to_numeric很好用,感觉是除了期中测试学到东西最多的一期!