Python实战之数字日期和时间的高级处理

Posted 山河已无恙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python实战之数字日期和时间的高级处理相关的知识,希望对你有一定的参考价值。

写在前面


  • 博文为《Python Cookbook》读书后笔记整理
  • 涉及内容包括:
    • 浮点数执行指定精度的舍入运算。
    • 执行精确的浮点数运算
    • 数字的格式化输出
    • 对数值进行取整
    • 二进制、八进制和十六进制整数转化输出
    • 从字节串中打包和解包大整数
    • 复数的数学运算
    • 处理无穷大和NaN
    • 处理大型数组的计算
    • 矩阵和线性代数的计算
    • 计算当前日期做后一个星期几的日期
    • 找出当月的日期范围
    • 将字符串转换为日期
    • 处理涉及到时区的日期问题
  • 理解不足小伙伴帮忙指正

傍晚时分,你坐在屋檐下,看着天慢慢地黑下去,心里寂寞而凄凉,感到自己的生命被剥夺了。当时我是个年轻人,但我害怕这样生活下去,衰老下去。在我看来,这是比死亡更可怕的事。--------王小波


数字、日期和时间的高级处理

嗯,学习发现有些方法函数即使大版本相同,小版本也是有些差距的,这是我学习的版本

┌──[root@liruilongs.github.io]-[~]
└─$python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.

浮点数执行指定精度的舍入运算。

对浮点数执行指定精度的舍入运算。

对于简单的舍入运算,使用内置的round(value, ndigits)函数即可。

┌──[root@liruilongs.github.io]-[~]
└─$python3
Python 3.6.8 (default, Nov 16 2020, 16:55:22)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> round(1.23, 1)
1.2
>>> round(1.25361,3)
1.254
>>>

当一个值刚好在两个边界的中间的时候, round 函数返回离它最近的偶数。对 1.5 或者 2.5 的舍入运算都会得到 2。

>>> round(1.5,0)
2.0
>>> round(2.5,0)
2.0
>>>

传给 round() 函数的 ndigits 参数可以是负数,舍入运算会作用在十位、百位、千位等上面

>>> a = 1627731
>>> round(a, -1)
1627730
>>> round(a, -2)
1627700
>>>

不要将舍入和格式化输出搞混淆了。如果你的目的只是简单的输出一定宽度的数,你不需要使用round()函数。而仅仅只需要在格式化的时候指定精度即可,

>>> x = 1.23456
>>> format(x, '0.2f')
'1.23'
>>> format(x, '0.4f')
'1.2346'
>>> 'value is :0.3f'.format(x)
'value is 1.235'
>>>

可以看到,输出的数据并不相同,一个是字符串,一个是int等数字类型

>>> type(format(1.5, '0.0f'))
<class 'str'>
>>> type(round(a, -1))
<class 'int'>
>>> type(round(1.25361,3))
<class 'float'>
>>>

不要试着去舍入浮点值来” 修正” 表面上看起来正确的问题。大多数使用到浮点的程序,没有必要也不推荐这样做.

>>> 2.1 + 4.2
6.300000000000001
>>> round(2.1 + 4.2,2)
6.3
>>>

执行精确的浮点数运算

要对浮点数执行精确的计算操作,并且不希望有任何小误差的出现。

浮点数的一个普遍问题是它们并不能精确的表示十进制数。并且,即使是最简单的数学运算也会产生小的误差

就那上面的Demo来讲

>>> 2.1 + 4.2
6.300000000000001
>>> round(2.1 + 4.2,2)
6.3
>>> (2.1 + 4.2) == 6.3
False
>>>

错误是由底层 CPU 和 IEEE 754 标准通过自己的浮点单位去执行算术时的特征。由于 Python 的浮点数据类型使用底层表示存储数据,因此你没办法去避免这样的误差。在Java里也出现同样的问题

public static void main(String[] args) 
       System.out.println(2.1+4.2);
   
// 6.300000000000001   

如果你想更加精确 (并能容忍一定的性能损耗),你可以使用 decimal 模块

>>> from decimal import Decimal
>>> a = Decimal('4.2')
>>> b = Decimal('2.1')
>>> a + b
Decimal('6.3')
>>> print(a + b)
6.3
>>> (a + b) == Decimal('6.3')
True
>>>

decimal 模块的一个主要特征是允许你控制计算的每一方面,包括数字位数和四舍五入运算。为了这样做,你先得创建一个本地上下文并更改它的设置

>>> from decimal import localcontext
>>> a = Decimal('1.3')
>>> b = Decimal('1.7')
>>> print(a / b)
0.7647058823529411764705882353
>>> with localcontext() as ctx:
... ctx.prec = 3
... print(a / b)
...
0.765
>>> with localcontext() as ctx:
... ctx.prec = 50
... print(a / b)
...
0.76470588235294117647058823529411764705882352941176
>>>

decimal 模块实现了 IBM 的” 通用小数运算规范”,Python 新手会倾向于使用 decimal 模块来处理浮点数的精确运算。但是要根据业务需求来处理,decimal 模块主要用在涉及到金融的领域。

  • 原生的浮点数计算要快的多
  • 在真实世界中很少会要求精确到普通浮点数能提供的 17 位精度

其他的一些误差,大数和小数的加法运算(在Java里也出现同样的问题)

>>> nums = [1.23e+18, 1, -1.23e+18]
>>> sum(nums)
0.0
>>> 1.23e+18 + 1 + -1.23e+18
0.0
>>> 1.23e+18 +  -1.23e+18 + 1
1.0
>>>
   public static void main(String[] args) 
       System.out.println(1.23e+18 + 1 + -1.23e+18);
    // 0.0

py的解决办法

>>> import math
>>> math.fsum(nums)
1.0
>>>

数字的格式化输出

你需要将数字格式化后输出,并控制数字的位数、对齐、千位分隔符和其他的细节。

格式化输出单个数字的时候,可以使用内置的format()函数,这个前面也有好的Demo,不多说

>>> x = 1234.56789
>>> format(x, '0.2f')
'1234.57'
>>> format(x, '>10.1f')
'    1234.6'
>>> format(x, '<10.1f')
'1234.6    '
>>> format(x, '0>10.1f')
'00001234.6'
>>> format(x, '^10.1f')
'  1234.6  '
>>> format(x, ',')
'1,234.56789'
>>> format(x, '0,.1f')
'1,234.6'
>>>

使用指数记法,将 f 改成 e 或者 E(取决于指数输出的大小写形式)

>>> format(x, 'e')
'1.234568e+03'
>>> format(x, '0.2E')
'1.23E+03'
>>>

同时指定宽度和精度的一般形式是 '[<>ˆ]?width[,]?(.digits)?' ,其中 width 和 digits 为整数,?代表可选部分。同样的格式也被用在字符串format()方法中。

>>> 'The value is :0,.2f'.format(x)
'The value is 1,234.57'
>>>

format() 函数 同时适用于浮点数和 decimal模块中的 Decimal 数字对象。

>>> x
1234.56789
>>> format(x, '0.1f')
'1234.6'
>>> format(-x, '0.1f')
'-1234.6'
>>>

包含千位符的格式化跟本地化没有关系。如果你需要根据地区来显示千位符,你需要自己去调查下 locale 模块中的函数了。你同样也

可以使用字符串的 translate()方法来交换千位符。

>>> swap_separators =  ord('.'):',', ord(','):'.' 
>>> format(x, ',').translate(swap_separators)
'1.234,56789'
>>> x
1234.56789
>>>

也可以直接使用% 来格式化数字的

>>> x
1234.56789
>>> '%0.2f' % x
'1234.57'
>>> '%10.1f' % x
'    1234.6'
>>> '%-10.1f' % x
'1234.6    '

二进制、八进制和十六进制整数转化输出

转换或者输出使用二进制,八进制或十六进制表示的整数。

为了将整数转换为二进制、八进制或十六进制文本串,可以分别使用bin() ,oct() 或 hex()函数:

>>> x = 1234
>>> bin(x)
'0b10011010010'
>>> oct(x)
'0o2322'
>>> hex(x)
'0x4d2'
>>>

不想输出 0b , 0o 或者 0x 的前缀的话,可以使用format()函数

>>> format(x, 'b')
'10011010010'
>>> format(x, 'o')
'2322'
>>> format(x, 'x')
'4d2'
>>>

整数是有符号的,所以如果你在处理负数的话,输出结果会包含一个负号

>>> x = -1234
>>> format(x, 'b')
'-10011010010'
>>> format(x, 'x')
'-4d2'
>>>

如果你想产生一个无符号值,你需要增加一个指示最大位长度的值。比如为了显示32 位的值,

>>> x = -1234
>>> format(2**32 + x, 'b')
'11111111111111111111101100101110'
>>> format(2**32 + x, 'x')
'fffffb2e'
>>>

为了以不同的进制转换整数字符串,简单的使用带有进制的int()函数即可:

>>> int('4d2', 16)
1234
>>> int('10011010010', 2)
1234
>>>

Python 指定八进制数的语法跟其他语言稍有不同

>>> import os
>>> os.chmod('script.py', 0755)
  File "<stdin>", line 1
    os.chmod('script.py', 0755)
                             ^
SyntaxError: invalid token
>>> os.chmod('app.py', 0o755)
>>

字节字符串到大整数的相互转化

你有一个字节字符串并想将它解压成一个整数。或者,你需要将一个大整数转换为一个字节字符串。

大整数和字节字符串之间的转换操作并不常见,一些场景也会用到,IPv6 网络地址使用一个 128 位的整数表示。如果你要从一个数据记录中提取这样的值的时候

处理一个拥有 128 位长的 16 个元素的字节字符串

>>> data = b'\\x00\\x124V\\x00x\\x90\\xab\\x00\\xcd\\xef\\x01\\x00#\\x004'
>>> len(data)
16
>>> int.from_bytes(data, 'little')
69120565665751139577663547927094891008
>>> int.from_bytes(data, 'big')
94522842520747284487117727783387188
>>>

为了将一个大整数转换为一个字节字符串,使用int.to_bytes()方法,并像下面这样指定字节数和字节顺序:

>>> x = 94522842520747284487117727783387188
>>> x.to_bytes(16, 'big')
b'\\x00\\x124V\\x00x\\x90\\xab\\x00\\xcd\\xef\\x01\\x00#\\x004'
>>> x.to_bytes(16, 'little')
b'4\\x00#\\x00\\x01\\xef\\xcd\\x00\\xab\\x90x\\x00V4\\x12\\x00'
>>>

这里字节顺序规则 (little 或 big) 仅仅指定了构建整数时的字节的低位高位排列方式

>>> x = 0x01020304
>>> x.to_bytes(4, 'big')
b'\\x01\\x02\\x03\\x04'
>>> x.to_bytes(4, 'little')
b'\\x04\\x03\\x02\\x01'
>>>

如果你试着将一个整数打包为字节字符串,那么它就不合适了,你会得到一个错误。如果需要的话,你可以使用 int.bit_length() 方法来决定需要多少字节位来存储这个值。(** 表示乘方,divmod() 函数把除数和余数运算结果结合起来,返回一个包含商和余数的元组(a // b, a % b)。)

>>> x = 523 ** 23
>>> x
335381300113661875107536852714019056160355655333978849017944067
>>> x.to_bytes(16, 'little')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: int too big to convert
>>> x.bit_length()
208
>>> nbytes, rem = divmod(x.bit_length(), 8)
>>> if rem:
... nbytes += 1
...
>>>
>>> x.to_bytes(nbytes, 'little')
b'\\x03X\\xf1\\x82iT\\x96\\xac\\xc7c\\x16\\xf3\\xb9\\xcf\\x18\\xee\\xec\\x91\\xd1\\x98\\xa2\\xc8\\xd9R\\xb5\\xd0'
>>>

复数的数学运算

使用复数来执行一些计算操作。

复数可以用使用函数complex(real, imag)或者是带有后缀j的浮点数来指定。

>>> a = complex(2, 4)
>>> b = 3 - 5j
>>> a
(2+4j)
>>> b
(3-5j)
>>>

对应的实部、虚部和共轭复数可以很容易的获取。

>>> a.real
2.0
>>> a.imag
4.0
>>> a.conjugate()
(2-4j)
>>>

常见的数学四则运算

>>> a + b
(5-1j)
>>> a * b
(26+2j)
>>> a / b
(-0.4117647058823529+0.6470588235294118j)
>>> abs(a)
4.47213595499958
>>>

如果要执行其他的复数函数比如正弦、余弦或平方根,使用 cmath 模块

>>> import cmath
>>> cmath.sin(a)
(24.83130584894638-11.356612711218174j)
>>> cmath.cos(a)
(-11.36423470640106-24.814651485634187j)
>>> cmath.exp(a)
(-4.829809383269385-5.5920560936409816j)
>>>

Python 中大部分与数学相关的模块都能处理复数。使用 numpy 很容易的构造一个复数数组并在这个数组上执行各种操作

>>> import numpy as np
>>> a = np.array([2+3j, 4+5j, 6-7j, 8+9j])
>>> a
array([ 2.+3.j, 4.+5.j, 6.-7.j, 8.+9.j])
>>> a + 2
array([ 4.+3.j, 6.+5.j, 8.-7.j, 10.+9.j])
>>> np.sin(a)
array([ 9.15449915 -4.16890696j, -56.16227422 -48.50245524j,
-153.20827755-526.47684926j, 4008.42651446-589.49948373j])
>>>

Python 的标准数学函数确实情况下并不能产生复数值,因此你的代码中不可能会出现复数返回值

>>> import math
>>> math.sqrt(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>

如果你想生成一个复数返回结果,你必须显示的使用 cmath 模块

>>> import cmath
>>> cmath.sqrt(-1)
1j
>>>

处理无穷大和NaN

你想创建或测试正无穷、负无穷或 NaN(非数字) 的浮点数。

Python 并没有特殊的语法来表示这些特殊的浮点值,但是可以使用float()来创建它们。比如:

>>> a = float('inf')
>>> b = float('-inf')
>>> c = float('nan')
>>> a
inf
>>> b
-inf
>>> c
nan
>>>

使用 math.isinf() 和 math.isnan() 函数来测试

>>> math.isinf(a)
True
>>> math.isnan(c)
True
>>>

无穷大数在执行数学计算的时候会传播

>>> a = float('inf')
>>> a + 45
inf
>>> a * 10
inf
>>> 10 / a
0.0
>>>

有些操作时未定义的并会返回一个 NaN 结果

>>> a = float('inf')
>>> a/a
nan
>>> b = float('-inf')
>>> a Pandas高级教程之:时间处理

举办“Python人工智能之Pytorch深度学习高级实战”远程直播研修班的通知

python实战技巧之两个不等长列表让里面的数字一一对应地相加

python实战技巧之两个列表实例中,如何让里面的数字一一对应地相加对于两个列表是等长的情况

#yyds干货盘点# 滚雪球学 Python 之怎么玩转时间和日期库

Elasticsearch高级检索之使用单个字母数字进行分词N-gram tokenizer(不区分大小写)实战篇