python还可以这样用
合适的方法可以简化代码提高效率
列表推导
例:创建了一个空列表,然后通过for循环来给列表添加新元素,并且需要通过if语句保证x在一定的数值范围
传统写法
data = []
for x in range(-5, 5):
if x >= -2:
data.append(x**2)
print(data)
列表推导
data = [x**2 for x in range(-5, 5) if x >= -2]
print(data)
优势:可以看出,相比于传统的代码编写,列表推导可以使代码更简洁
函数使用
例:求数组a,b的欧几里得相似度
import numpy as np
def eculidDisSim(x, y):
return np.sqrt(sum(pow(a-b, 2) for a, b in zip(x, y)))
a = np.array([1, 2, 3])
b = np.array([6, 5, 4])
print(eculidDisSim(a, b))
优势:函数能提高应用的模块性,和代码的重复利用率。比如封装一个文本分词函数、相似性计算函数等
异常处理
每当在运行时检测到程序错误时,python就会引发异常。可以使用try...except... 进行异常处理。
try:
# 执行try代码
except:
# 执行应对异常发生时的代码
join和split
join用来连接字符串,split恰好相反, 拆分字符串的。常用于字符串数据处理
strs = ' '.join(['my', 'name', 'is', 'peiqi'])
print(strs)
li = 'my name is qiaozhi'.split(' ')
print(li)
lambda函数
lambda函数是一种比较小的匿名函数,其功能是执行某种简单的表达式或运算,而无需完全定义函数。
func = lambda x, y : x * y
print(func(2, 3))
func = lambda x : x ** 2 + 3
print(func(2))
map函数
map()是一种内置的Python函数,方法会将一个函数映射到序列的每一个元素上,生成新序列,包含所有函数返回值。
def func(x):
return x ** 2
squ = list(map(func, [1, 2, 3, 4, 5]))
print(squ)
filter函数
filter() 函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表。
def func(x):
return x % 2 == 1
li = filter(func, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(list(li))
itertools模块
itertools模块中 的函数大多是返回各种迭代器对象,其中很多函数的作用需要我们平时要写很多代码才能达到,然而在运行效率上反而更低。
import itertools
x = itertools.accumulate(range(10)) # 累加
print(list(x))
x = itertools.chain(range(3), range(4), [8, 9, 10]) # 合并列表
print(list(x))
x = itertools.product('AB', range(2)) # 笛卡尔积
print(list(x))
除了以上几个基础方法外,数据分析常用的3个库:numpy、pandas和matplotlib详解可以参考下面这篇博客。
高效处理带有时间序列数据
什么是时间序列?
时间序列( Time Series)是-种重要的结构化数据形式,在多个时间点观察或测量到的任何事物都可以形成一段时间序列,分为:
- 定期的时间序列:数据点是根据某种规律定期出现的(每10秒、每15分钟)
假设我们定期每秒获取一次数据,那一分钟内我们将获取到6条带时间序列的数据
- 不定期的时间序列:没有固定的时间单位或单位之间的偏移量
数据的获取可能是一分钟一条或者是20秒一条
日期和时间数据
- Python标准库包含用于日期(date) 和时间( time)数据的数据类型,主要用到的模块有datetime和time。
- Pandas提供了一组标准的时间序列处理工具和数据算法,如进行切片/切块、聚合、对定期/不定期的时间序列进行重采样等。
datetime模块中的数据类型
| 类型 | 说明 |
|---|---|
| date | 以公历形式存储日历日期(年、月、日) |
| time | 将时间存储为时、分、秒、毫秒 |
| datetime | 存储日期和时间 |
| timedelta | 表示两个datetime值之间的差(日、秒、毫秒) |
datetime.datetime或简称为datetime,是用的最多的数据类型
datetime类型
- 查看现在的日期和时间(记录到毫秒),为datetime类型
from datetime import datetime
now = datetime.now()
print(now)
print(type(now))
- 查看年月日
from datetime import datetime
now = datetime.now()
print(now.year, now.month, now.day)
timedelta类型
- 两个datetime对象之间的时间差(日、秒、毫秒)为timedelta类型
from datetime import timedelta, datetime
now = datetime.now()
delta = now - datetime(2022, 12, 31)
print(delta)
print(type(delta))
- 查看相差的日、秒、毫秒
from datetime import timedelta, datetime
now = datetime.now()
delta = now - datetime(2022, 12, 31)
print(delta.days, delta.seconds, delta.microseconds)
常用于特征构建
- 给datetime对象加上或减去一个或多个timedelta,会产生一个新的对象。
from datetime import timedelta, datetime
now = datetime.now()
new = now + timedelta(10)
print(new)
print(type(new))
new = now - 2 * timedelta(10)
print(new)
字符串和datetime的相互转化
在日常数据处理中,经常会出现把日期储存成字符串或把字符串储存成日期的情况
字符串与datatime之间通常需要进行转换,有以下三种转换方式:
-
Python标准库函数:
- 日期转换成字符串:利用str或strftime
- 字符串转换成日期: datetime.strptime
-
第三方库函数:
- dateutil.parser时间解析函数
日期转换成字符串
- 利用str将日期转换成字符串
from datetime import datetime
stamp = datetime.now()
print(str(stamp))
- 更改datetime格式
| 符号 | 描述 | 范围 |
|---|---|---|
| %y | 两位数的年份表示 | 0~99 |
| %Y | 四位数的年份表示 | 0000~9999 |
| %m | 月份 | 01~12 |
| %d | 月份内的一天 | 0~31 |
| %H | 24小时制小时 | 0~23 |
| %l | 12小时制小时 | 01~12 |
| %M | 分钟数 | 00~59 |
| %S | 秒数 | |
| %a | 本地简化星期名称 | |
| %A | 本地完整星期名称 | |
| %b | 本地简化月份名称 | |
| %B | 本地完整月份名称 | |
| %c | 本地相应的日期表示和时间表示 | |
| %j | 年内的一天 | 001~366 |
| %p | 本地A.M.或者P.M.的等价符 | |
| %U | 一年中的星期数,星期天为一周的开始 | 0~53 |
| %w | 星期,星期天为一周的开始 | 0~6 |
| %W | 一年中的星期数,星期一为一周的开始 | 0~53 |
| %x | 本地对应的日期表示 | |
| %X | 本地相应的时间表示 | |
| %Z | 当前时区的名称 | |
| %% | %号本身 |
- 利用strftime将日期转换为字符串,并设置格式
from datetime import datetime
stamp = datetime.now()
print(stamp.strftime('%Y-%m-%d'))
字符串转换成日期
- 利用strptime将字符串转换成日期,需传入格式
from datetime import datetime
value = '2023-01-08'
print(datetime.strptime(value, '%Y-%m-%d'))
pandas时间序列基础
- Pandas最基本的时间序列类型就是以时间戳(通常以Python字符串或datetime对象表示)为索引的Series
import pandas as pd
import numpy as np
dates = ['2017-06-20', '2017-06-21', '2017-06-22', '2017-06-23', '2017-06-24', '2017-06-25', '2017-06-26', '2017-06-27']
ts= pd.Series(np.random.randn(8), index=pd.to_datetime(dates))
print(ts)
通过字符串选取子集
注意: 通过字符串选取子集时,传入的字符串需要能被解析成日期
import pandas as pd
import numpy as np
dates = ['2017-06-20', '2017-06-21', '2017-06-22', '2017-06-23', '2017-06-24', '2017-06-25', '2017-06-26', '2017-06-27']
ts= pd.Series(np.random.randn(8), index=pd.to_datetime(dates))
print(ts)
print(ts['2017-06-22'])
print(ts['22/06/2017'])
第二种格式可以查询出结果,但是会发出警告
- 可传入只包含年或年月的字符串,选取该范围内的子集
import pandas as pd
import numpy as np
dates = ['2017-06-20', '2017-06-21', '2017-06-22', '2017-06-23', '2017-06-24', '2017-06-25', '2017-06-26', '2017-06-27']
ts= pd.Series(np.random.randn(8), index=pd.to_datetime(dates))
print(ts)
print(ts['2017-06'])
- 选取时间范围内的子集
import pandas as pd
import numpy as np
dates = ['2017-06-20', '2017-06-21', '2017-06-22', '2017-06-23', '2017-06-24', '2017-06-25', '2017-06-26', '2017-06-27']
ts= pd.Series(np.random.randn(8), index=pd.to_datetime(dates))
print(ts)
print(ts['2017-06-20': '2017-06-24'])
带有重复索引的时间序列
在某些应用场景中,通常存在一一个时间点上有多个观测数据的情况,例如
import pandas as pd
import numpy as np
dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03'])
dup_ts = pd.Series(np.arange(5), index=dates)
print(dup_ts)
当索引唯一
import pandas as pd
import numpy as np
dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03'])
dup_ts = pd.Series(np.arange(5), index=dates)
print(dup_ts['2023-01-01'])
当索引不唯一
import pandas as pd
import numpy as np
dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03'])
dup_ts = pd.Series(np.arange(5), index=dates)
print(dup_ts['2023-01-02'])
针对上述情况,通常使用如下两种方式进行操作:
- index.is_unique检查 索引日期是否唯一
import pandas as pd
import numpy as np
dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03'])
dup_ts = pd.Series(np.arange(5), index=dates)
print(dup_ts.index.is_unique)
-
使用groupby() 对数据进行分组聚合
- 使用groupby()对数据进行分组,并传入level= 0(索引的唯一一层)
-
import pandas as pd import numpy as np dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03']) dup_ts = pd.Series(np.arange(5), index=dates) grouped = dup_ts.groupby(level=0) print(pd.Series(grouped)) - 之后可使用mean()和count()等函数对分组后的数据按时间戳进行聚合
-
import pandas as pd import numpy as np dates = pd.DatetimeIndex(['2023-01-01', '2023-01-02', '2023-01-02', '2023-01-03', '2023-01-03']) dup_ts = pd.Series(np.arange(5), index=dates) grouped = dup_ts.groupby(level=0) print(grouped.mean()) print(grouped.count())
日期的范围、频率及移动
pd.date_range() 可用 于生成指定长度的日期索引,默认产生按天计算的时间点,即日期范围。参数可以是:
- 起始结束日期
- 或者只有一个起始或结束日期,加一个时间段参数
起始结束日期
import pandas as pd
print(pd.date_range('20230101', '20230110'))
print(pd.date_range(start='20230101', periods=10))
print(pd.date_range(end='20230110', periods=10))
三者输出的结果是一样的
生成日期范围
如果想要生成一一个特殊频率的日期索引,如一个由每月最后一个 工作日组成的日期索引,直接加上参数freq='BM' ( BM表示business end of month)
import pandas as pd
print(pd.date_range('20230101', '20231201', freq='BM'))
时间频率
Pandas中的频率是由一个基础频率和一个乘数组成的。基础频率通常以一个字符串别名表示,如'M'表示每月, ‘H’表示每小时。
import pandas as pd
print(pd.date_range('20230101', '20231201', freq='3M'))
常于分析周期性数据时使用
基础频率表
| 别名 | 偏移量类型 | 说明 |
|---|---|---|
| D | Day | 每日历日 |
| B | BusinessDay | 每工作日 |
| H | Hour | 每小时 |
| T或min | Minute | 每分钟 |
| M | MonthEnd | 每月最后一个日历日 |
| BM | BusinessMonthEnd | 每月最后一个工作日 |
| WOM-1MON | WeekOfMonth | 产生每月第一、第二、第三或第四周的星期几,如WOM-3FRI表示每月第3个星期五 |
| Q-JAN | QuarterEnd | 对于指定月份( JAN、FEB、MAR.....DEC)结束的年度,每季度最后一个月的最后一个日历日 |
| A-JAN | YearEnd | 对于指定月份的最后一个日历日 |
日期偏移量
对于每个基础频率,都有一个被称为日期偏移量( date offset) 的对象与之对应。可以通过实例化日期偏移量来创建某种频率:
from pandas.tseries.offsets import Hour,Minute
hour = Hour()
print(hour)
four_hours = Hour(4)
print(four_hours)
大部分偏移量对象可以通过加法进行连接:
from pandas.tseries.offsets import Hour,Minute
print(Hour(2) + Minute(30))
有些频率所描述的时间点并不是均匀分隔的。例如‘M’和‘BM'就取决于每月的天数,对于后者,还要考虑月末是不是周末,将这些称为锚点偏移量( anchored offset )
对于这种情况,我们需要移动(超前或者滞后)数据
移动(超前或者滞后)数据
移动( shifting) 指的是沿着时间轴将数据前移或后移,保持索引不变。
import pandas as pd
import numpy as np
ts = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2018', periods=4, freq='M'))
print(ts)
shift(): 保持索引不变数据前移/后移
import pandas as pd
import numpy as np
ts = pd.Series(np.random.randn(4), index=pd.date_range('1/1/2018', periods=4, freq='M'))
print(ts)
print(ts.shift(2))
print(ts.shift(-2))
- 说明:
- ts.shift(2)把ts的结果往后移两位,即2018-01-31的数据移动至2018-03-31,2018-02-28的数据移动至2018-04-30,由于2018-01-31和2018-02-28不存在前两位的数据,因此移动后两者数据为NaN。同理,ts.shift(-2)把ts的结果往前移两位,因此2018-03-31和2018-04-30两者数据为NaN
通过偏移量对日期进行位移
将当前时间now直接加.上一个实例化的日期偏移量,即可得到相应偏移后的时间戳
from pandas.tseries.offsets import Day, MonthEnd
from datetime import datetime
now = datetime.now().date()
print(now)
print(now + 3 * Day())