【pandas】17 数据处理和绘图
2023.1.16 pandas数据处理方法和绘图:读取数据、更改数据、时间数据等 主要参考:https://mofanpy.com/tutorials/data-manipulation/pandas/time
17.1运算方法
17.1.1 筛选赋值运算
就是用前面的方法对数据进行筛选,然后对值进行一定的操作(对数据修改)
本章实例就是通过不同方法筛选出需要的位置,进行赋值
import pandas as pd
import numpy as np
data = np. arange( - 12 , 12 ) . reshape( ( 6 , 4 ) )
df = pd. DataFrame(
data,
index= list ( "abcdef" ) ,
columns= list ( "ABCD" ) )
df
A B C D a -12 -11 -10 -9 b -8 -7 -6 -5 c -4 -3 -2 -1 d 0 1 2 3 e 4 5 6 7 f 8 9 10 11
df[ "A" ] *= 0
df
A B C D a 0 -11 -10 -9 b 0 -7 -6 -5 c 0 -3 -2 -1 d 0 1 2 3 e 0 5 6 7 f 0 9 10 11
df. loc[ 'a' , 'A' ] = 100
df. iloc[ 1 , 0 ] = 200
df
A B C D a 100 -11 -10 -9 b 200 -7 -6 -5 c 0 -3 -2 -1 d 0 1 2 3 e 0 5 6 7 f 0 9 10 11
df. iloc[ 0 , : ] *= 2
df
A B C D a 200 -22 -20 -18 b 200 -7 -6 -5 c 0 -3 -2 -1 d 0 1 2 3 e 0 5 6 7 f 0 9 10 11
df[ "A" ] [ df[ "A" ] == 0 ] = 1
df
A B C D a 200 -22 -20 -18 b 200 -7 -6 -5 c 1 -3 -2 -1 d 1 1 2 3 e 1 5 6 7 f 1 9 10 11
17.1.2 apply
数据.apply(方程式,。。)
:通过方程式的筛选选择,批量修改
df
A B C D a 200 -22 -20 -18 b 200 -7 -6 -5 c 1 -3 -2 -1 d 1 1 2 3 e 1 5 6 7 f 1 9 10 11
df. apply ( np. sqrt)
A B C D a 14.142136 NaN NaN NaN b 14.142136 NaN NaN NaN c 1.000000 NaN NaN NaN d 1.000000 1.000000 1.414214 1.732051 e 1.000000 2.236068 2.449490 2.645751 f 1.000000 3.000000 3.162278 3.316625
def func ( x) :
return x[ 0 ] * 2 , x[ 1 ] * - 1
df. apply ( func, axis= 1 , result_type= 'expand' )
0 1 a 400 22 b 400 7 c 2 3 d 2 -1 e 2 -5 f 2 -9
df. iloc[ 0 ]
A 200
B -22
C -20
D -18
Name: a, dtype: int32
result_type='expand'
:让输出的结果可以生成多 column。如下:
def func ( x) :
return x[ 0 ] * 2 , x[ 1 ] * - 1
df. apply ( func, axis= 1 )
a (400, 22)
b (400, 7)
c (2, 3)
d (2, -1)
e (2, -5)
f (2, -9)
dtype: object
如果 reult_type="broadcast"
,那么原 column 和 index 名会继承到新生成的数据中;没有的话本例column
是0-3
因为广播,这个return必须凑齐colunm个数,即使没变化,也要写上,不然报错
print ( "df:\n" , df)
def func ( x) :
return x[ 0 ] * 2 , x[ 1 ] * - 1 , x[ 2 ] , x[ 3 ]
print ( "-----" )
print ( df. apply ( func, axis= 1 , result_type= 'expand' ) )
df. apply ( func, axis= 1 , result_type= 'broadcast' )
df:
A B C D
a 200 -22 -20 -18
b 200 -7 -6 -5
c 1 -3 -2 -1
d 1 1 2 3
e 1 5 6 7
f 1 9 10 11
-----
0 1 2 3
a 400 22 -20 -18
b 400 7 -6 -5
c 2 3 -2 -1
d 2 -1 2 3
e 2 -5 6 7
f 2 -9 10 11
A B C D a 400 22 -20 -18 b 400 7 -6 -5 c 2 3 -2 -1 d 2 -1 2 3 e 2 -5 6 7 f 2 -9 10 11
def func ( r) :
return r[ 2 ] * 4
last_row = df. apply ( func, axis= 0 )
print ( "last_row:\n" , last_row)
df. iloc[ 2 , : ] = last_row
print ( "\ndf:\n" , df)
last_row:
A 16
B -48
C -32
D -16
dtype: int64
df:
A B C D
a 200 -22 -20 -18
b 200 -7 -6 -5
c 16 -48 -32 -16
d 1 1 2 3
e 1 5 6 7
f 1 9 10 11
注意:
默认是对列进行操作,也就是 axis=1
,是按行
来操作的,是对每行的第3
个数操作,所以输出你看这变化是列
; axis=0
是对列
的第3
个数操作,反而输出变化的是行
; 这个别弄反了
17.2 文字处理
17.2.1 格式化字符
str.upper(); str.lower()
; 全大写全小写str.len()
:长度str.strip(); str.lstrip(); str.rstrip()
去掉两边空格。去掉左空格、有空格str.split()
数据用什么分割
我们用这些处理字符的时候,你要确保 Series 或者 DataFrame 的 dtype="string"
pd_not_s = pd. Series(
[ "A" , "B" , "C" , "Aaba" , "Baca" , "CABA" , "dog" , "cat" ] ,
)
print ( "pd_not_s type:" , pd_not_s. dtype)
pd_s = pd_not_s. astype( "string" )
print ( "pd_s type:" , pd_s. dtype)
pd_not_s type: object
pd_s type: string
用法很简单,举几个简单的例子:
print ( "\npandas:\n" , pd_s. str . upper( ) )
pandas:
0 A
1 B
2 C
3 AABA
4 BACA
5 CABA
6 DOG
7 CAT
dtype: string
print ( "\npandas len:\n" , pd_s. str . len ( ) )
pandas len:
0 1
1 1
2 1
3 4
4 4
5 4
6 3
7 3
dtype: Int64
py_s = [ "a_b_c" , "jill_jesse" , "frank" ]
pd_s = pd. Series( py_s, dtype= "string" )
print ( "\npandas split:\n" , pd_s. str . split( "_" ) )
pandas split:
0 [a, b, c]
1 [jill, jesse]
2 [frank]
dtype: object
**【和python比较】**我们用python,一般形式是这样的(除了大小写)
print ( "python len:\n" , [ len ( s) for s in py_s] )
print ( "python split:\n" , [ s. split( "_" ) for s in py_s] )
python len:
[5, 10, 5]
python split:
[['a', 'b', 'c'], ['jill', 'jesse'], ['frank']]
pd_s. str . split( "_" , expand= True )
0 1 2 0 a b c 1 jill jesse <NA> 2 frank <NA> <NA>
17.2.2 正则方案
我们用 str.contains()
或 str.match()
来确认它真的找到了匹配文字
str.contains()
:有就识别出来str.match()
:完全匹配
pattern = r"[0-9][a-z]"
s = pd. Series( [ "1" , "1a" , "11c" , "abc" ] , dtype= "string" )
s. str . contains( pattern)
0 False
1 True
2 True
3 False
dtype: boolean
- ` r"[0-9][a-z]" `: 这里面【条件1 】【条件2 】两个条件都要有。
- `11c`识别出来了`1c`, 用`contains`是`True `, 用`match `是`False `
s. str . match ( pattern)
0 False
1 True
2 False
3 False
dtype: boolean
17.3 异常数据处理
17.3.1 找到NaN数据
pd.isna(), pd.notna()
import pandas as pd
import numpy as np
df = pd. DataFrame( [ [ 1 , None ] , [ np. nan, 4 ] ] )
df
df. isna( )
【找不是NaN数据】 df.notna()
或者~df.isna()
df. notna( )
17.3.2 处理NaN
df.dropna()
:移除df.fillna()
:填充df.clip()
:不符合范围的填充
df = pd. DataFrame( {
"a" : [ 1 , None , 3 ] ,
"b" : [ 4 , 5 , 6 ]
} )
df
- 移除,** 默认** 是`axis= 0 `, 列方向改变;
df. dropna( )
df. dropna( axis= 1 )
a_mean = df[ "a" ] . mean( )
new_col = df[ "a" ] . fillna( a_mean)
df[ "a" ] = new_col
df
df = pd. DataFrame( {
"a" : [ 1 , None , 3 , None ] ,
"b" : [ 4 , 8 , 12 , 12 ]
} )
a_nan = df[ "a" ] . isna( )
a_new_value = df[ "b" ] [ a_nan] / 4
print ( "返回了索引和值:\n" , a_new_value)
new_col = df[ "a" ] . fillna( a_new_value)
df[ "a" ] = new_col
df
返回了索引和值:
1 2.0
3 3.0
Name: b, dtype: float64
`[ "b" ] [ a_nan] / 4 `: b中是NaN的值/ 4
有些值大的离谱小的离谱,这些超出的值会给一个上下的阈值
df = pd. DataFrame( {
"a" : [ 1 , 1 , 2 , 1 , 2 , 40 , 1 , 2 , 1 ] ,
} )
df[ "a" ] = df[ "a" ] . clip( lower= 0 , upper= 3 )
df
17.4 时间数据
17.4.1 读时间序列
pd.to_datetime()
:pandas数据中有时间,但是它不知道是时间序列 所以要转换一下
import pandas as pd
df = pd. DataFrame( {
"time" : [ "2022/03/12" , "2022/03/13" , "2022/03/14" ] ,
"value" : [ 1 , 2 , 3 ]
} )
print ( df)
time value
0 2022/03/12 1
1 2022/03/13 2
2 2022/03/14 3
pd. to_datetime( df[ "time" ] )
0 2022-03-12
1 2022-03-13
2 2022-03-14
Name: time, dtype: datetime64[ns]
pd. to_datetime(
[
"1@21@2022%%11|11|32" ,
"12@01@2022%%44|02|2" ,
"4@01@2022%%14|22|2"
] ,
format = "%m@%d@%Y%%%%%S|%H|%M"
)
DatetimeIndex(['2022-01-21 11:32:11', '2022-12-01 02:02:44',
'2022-04-01 22:02:14'],
dtype='datetime64[ns]', freq=None)
17.4.2 自建时间序列datetime
【类似于range】:pd.date_range(start, end)
freq="48h"
:间隔时间。(没有)默认是一天
import datetime
start = datetime. datetime( 2022 , 3 , 12 )
end = datetime. datetime( 2022 , 3 , 18 )
index = pd. date_range( start, end, freq= "48h" )
index
DatetimeIndex(['2022-03-12', '2022-03-14', '2022-03-16', '2022-03-18'], dtype='datetime64[ns]', freq='48H')
print ( "\n\npd.date_range(start, end, periods=5)\n" ,
pd. date_range( start, end, periods= 5 )
)
pd.date_range(start, end, periods=5)
DatetimeIndex(['2022-03-12 00:00:00', '2022-03-13 12:00:00',
'2022-03-15 00:00:00', '2022-03-16 12:00:00',
'2022-03-18 00:00:00'],
dtype='datetime64[ns]', freq=None)
17.4.3 选取时间data_range
做了一个在时间上随机生成正态分布的值
start = datetime. datetime( 2022 , 3 , 1 )
end = datetime. datetime( 2022 , 3 , 23 )
rng = pd. date_range( start, end)
ts = pd. Series( np. random. randn( len ( rng) ) , index= rng)
ts
2022-03-01 0.471139
2022-03-02 -1.101305
2022-03-03 -0.544299
2022-03-04 1.244004
2022-03-05 -1.594690
2022-03-06 1.686098
2022-03-07 -0.613820
2022-03-08 -2.399893
2022-03-09 1.132414
2022-03-10 -1.198595
2022-03-11 0.779643
2022-03-12 0.265703
2022-03-13 -0.545246
2022-03-14 1.518926
2022-03-15 0.255980
2022-03-16 0.876557
2022-03-17 -0.599384
2022-03-18 -0.351991
2022-03-19 0.033286
2022-03-20 0.085700
2022-03-21 0.421871
2022-03-22 -1.981706
2022-03-23 0.209435
Freq: D, dtype: float64
index=rng 这里面把时间放在了index上
在这个时间序列下,可以利用各种方式 切片,直接输入等来取一些值
ts[ 1 : 8 ] . plot( )
<AxesSubplot:>
import datetime
t1 = datetime. datetime( 2022 , 3 , 12 )
t2 = datetime. datetime( 2022 , 3 , 18 )
ts[ t1: t2] . plot( )
<AxesSubplot:>
ts[ "2022-03-12" : "2022-03-18" ] . plot( )
<AxesSubplot:>
17.4.4 时间运算pd.Timedelta .dayofyear .strftime
比如我想复制一份这周的表格,给下周用, 我就直接将这个月 copy 过来,然后日期上加一周时间。
rng = pd. date_range( "2022-01-01" , "2022-01-07" )
rng + pd. Timedelta( weeks= 1 )
DatetimeIndex(['2022-01-08', '2022-01-09', '2022-01-10', '2022-01-11',
'2022-01-12', '2022-01-13', '2022-01-14'],
dtype='datetime64[ns]', freq='D')
`weeks`参数,还可以用 `days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds` 这些
import pandas as pd
rng = pd. date_range( "2022-2-08" , "2022-2-14" )
rng. dayofyear
Int64Index([39, 40, 41, 42, 43, 44, 45], dtype='int64')
除此之外,还有:rng.dayofweek; rng.weekofyear; rng.weekday
rng. strftime( "%m/%d/%Y" )
Index(['02/08/2022', '02/09/2022', '02/10/2022', '02/11/2022', '02/12/2022',
'02/13/2022', '02/14/2022'],
dtype='object')
.day_name(); .month_name()
:看是周几或者那个月
rng. day_name( )
Index(['Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday',
'Monday'],
dtype='object')
17.4.5 时区
s = pd. to_datetime(
[ "2022/03/12 22:11" , "2022/03/12 12:11" , "2022/03/12 2:11" ]
)
s_us = s. tz_localize( "America/New_York" )
s_us
DatetimeIndex(['2022-03-12 22:11:00-05:00', '2022-03-12 12:11:00-05:00',
'2022-03-12 02:11:00-05:00'],
dtype='datetime64[ns, America/New_York]', freq=None)
rng = pd. date_range(
"2022-01-08" , "2022-01-11" ,
tz= "America/New_York" )
rng
s_cn = s_us. tz_convert( "Asia/Shanghai" )
s_cn
DatetimeIndex(['2022-03-13 11:11:00+08:00', '2022-03-13 01:11:00+08:00',
'2022-03-12 15:11:00+08:00'],
dtype='datetime64[ns, Asia/Shanghai]', freq=None)
17.5 绘制图表
散点图 scatter
折线图 plot
条形图 .plot.bar()
分布图 .plot.hist()
饼图 plot.Pie
面积图 plot.area
用法就是 df.图片名字.什么图
17.5.1 散点图Scatter
c
: 对于这组数据中每个(x,y)数据点的颜色值s
: 画点的大小(size)alpha
:不透明度cmap
:colormap,你可以在这里找到非常丰富的案例.我这里觉得应该是选哪个调色板或色彩格式
import numpy as np
import pandas as pd
n = 10
df = pd. DataFrame( {
"x" : np. random. normal( 0 , 1 , n) ,
"y" : np. random. normal( 0 , 1 , n) ,
} )
df. plot. scatter( x= "x" , y= "y" , c= color, s= 60 , alpha= .5 , cmap= "rainbow" )
<AxesSubplot:xlabel='x', ylabel='y'>
17.5.2 散点图 plot.Scatter()
n = 20
x = np. linspace( - 1 , 1 , n)
y1 = x * - 1 - 0.1 + np. random. normal( 0 , 0.3 , n)
y2 = x * 2 + 0.4 + np. random. normal( 0 , 0.3 , n)
df = pd. DataFrame( {
"x" : x,
"y1" : y1,
"y2" : y2,
} )
df. plot( x= "x" , y= [ "y1" , "y2" ] , alpha= .9 )
<AxesSubplot:xlabel='x'>
更改的地方:透明度改成了9
17.5.3 条形图Bar
df = pd. DataFrame( np. random. rand( 5 , 3 ) , columns= [ "a" , "b" , "c" ] )
print ( df)
df. plot. bar( )
a b c
0 0.768000 0.963485 0.460891
1 0.824841 0.599105 0.607710
2 0.372849 0.541759 0.114071
3 0.278834 0.283976 0.661167
4 0.570884 0.638697 0.581616
<AxesSubplot:>
df. plot. bar( stacked= True )
<AxesSubplot:>
df. plot. barh( )
<AxesSubplot:>
17.5.4 分布图 plot.hist()
分布图在机器学习和统计学中非常重要,我经常画分布图,比如要画 神经网络的参数分布可视化。 又或者是 GAN 生成对抗网络当中的数据分布。
df = pd. DataFrame(
{
"a" : np. random. randn( 1000 ) + 1 ,
"b" : np. random. randn( 1000 ) ,
"c" : np. random. randn( 1000 ) - 4 ,
}
)
df. plot. hist( alpha= 0.3 , bins= 60 )
<AxesSubplot:ylabel='Frequency'>
bins
柱状体的数量
17.5.5 饼图plot.Pie
df = pd. DataFrame(
{
"bigBoss" : np. random. rand( 4 ) ,
"smallBoss" : np. random. rand( 4 ) ,
} ,
index= [ "meeting" , "supervise" , "teaching" , "team building" ] ,
)
df. plot. pie( subplots= True , figsize= ( 9 , 9 ) , legend= False )
array([<AxesSubplot:ylabel='bigBoss'>, <AxesSubplot:ylabel='smallBoss'>],
dtype=object)
subplots=True, figsize=(9,9), legend=False
:
这里是两组数据,要加subplots,是分开画意思; legend 是用来确定要不要输出图例的。也就是有个图 来说什么颜色代表什么
17.5.6面 积图plot.area()
df = pd. DataFrame(
np. random. rand( 10 , 4 ) ,
columns= [ "a" , "b" , "c" , "d" ]
)
df. plot. area( )
<AxesSubplot:>
如果你不想上下堆砌在一起观看,而是有统一的一个起点,那可以用这个参数 stacked=False
。
df. plot. area( stacked= False )
<AxesSubplot:>