Pandas进阶捌 文本处理

Pandas进阶捌 文本处理

pandas进阶系列根据datawhale远昊大佬的joyful pandas教程写一些自己的心得和补充
本文部分引用了原教程,并参考了

  • 《利用Python进行数据分析》
  • pandas官网
  • python官网
  • learn-regex-zh

另注:本文是对joyful pandas教程的延伸,完整理解需先阅读joyful pandas教程第八章

五、练习

Ex1:房屋信息数据集

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

  1. year列改为整数年份存储。
  2. floor列替换为Level, Highest两列,其中的元素分别为string类型的层类别(高层、中层、低层)与整数类型的最高层数。
  3. 计算房屋每平米的均价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元/平米

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

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

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?
  1. 计算每一个Episode的台词条数。
  2. 以空格为单词的分割符号,请求出单句台词平均单词量最多的前五个人。
  3. 若某人的台词中含有问号,那么下一个说台词的人即为回答者。若上一人台词中含有 n n n个问号,则认为回答者回答了 n n n个问题,请求出回答最多问题的前五个人。

【第一问】
这里因为分组好几次都出现了KeyError,所以查看下各列的名称,发现SeasonEpsode列分别在前面和后面有空格,因此需要预处理一下

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很好用,感觉是除了期中测试学到东西最多的一期!

你可能感兴趣的:(pandas,datawhale,正则表达式,python,正则表达式,pandas)