Kaggle课程官方链接:Working with External Libraries
本专栏旨在Kaggle官方课程的汉化,让大家更方便地看懂。
Working with External Libraries
导入、运算符重载和进入外部库世界的生存技巧
在本教程中,您将学习Python中的导入,获得一些使用不熟悉的库(以及它们返回的对象)的技巧,并深入了解运算符重载。
Imports
到目前为止,我们已经讨论了语言内置的类型和函数。
但是Python最好的一点(特别是如果你是一名数据科学家)是为它编写了大量高质量的自定义库。
其中一些库位于“标准库”中,这意味着您可以在运行Python的任何地方找到它们。其他库可以很容易地添加,即使它们并不总是与Python一起提供。
无论哪种方式,我们都将通过导入访问此代码。
我们将从标准库导入数学来开始我们的示例。
import mathprint("It's math! It has type {}".format(type(math)))
It's math! It has type <class 'module'>
数学是一个模块。模块只是由其他人定义的变量(如果你愿意,可以称之为命名空间)的集合。我们可以使用内置函数dir()查看数学中的所有名称。
print(dir(math))
['__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'gcd', 'hypot', 'inf', 'isclose', 'isfinite', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'log2', 'modf', 'nan', 'pi', 'pow', 'radians', 'remainder', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'tau', 'trunc']
我们可以使用点语法访问这些变量。其中一些引用了简单的值,比如math.pi:
print("pi to 4 significant digits = {:.4}".format(math.pi))
pi to 4 significant digits = 3.142
但我们在模块中发现的大部分都是函数,比如math.log:
math.log(32, 2)
5.0
当然,如果我们不知道math.log的作用,我们可以对它调用help():
help(math.log)
Help on built-in function log in module math:log(...)log(x, [base=math.e])Return the logarithm of x to the given base.If the base not specified, returns the natural logarithm (base e) of x.
我们也可以对模块本身调用help()。这将为我们提供模块中所有函数和值的组合文档(以及模块的高级描述)。运行结果太多,这里不演示了,自己可以多多尝试。
help(math)
Other import syntax
如果我们知道我们将经常使用数学中的函数,我们可以在较短的别名下导入它以节省一些打字时间(尽管在这种情况下“math”已经很短了)。
import math as mt
mt.pi
3.141592653589793
您可能已经看到过使用某些流行库(如Pandas、Numpy、Tensorflow或Matplotlib)执行此操作的代码。例如,将numpy作为np导入和将pandas作为pd导入是一种常见的约定。
as只是重命名导入的模块。这相当于做以下事情:
import math
mt = math
如果我们能自己引用数学模块中的所有变量,那岂不是很棒?也就是说,如果我们可以只引用pi而不是math.pi或mt.pi?好消息:我们可以做到。
from math import *
print(pi, log(32, 2))
3.141592653589793 5.0
import*使您可以直接访问模块的所有变量(没有任何虚线前缀)。
坏消息:一些纯粹主义者可能会抱怨你这样做。
更糟糕的是:他们有点道理。
from math import *
from numpy import *
print(pi, log(32, 2))
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /tmp/ipykernel_19/3018510453.py in <module>1 from math import *2 from numpy import * ----> 3 print(pi, log(32, 2))TypeError: return arrays must be of ArrayType
发生了什么事?它以前奏效过!
这些类型的“明星导入”偶尔会导致奇怪、难以调试的情况。
在这种情况下,问题在于math和numpy模块都有名为log的函数,但它们具有不同的语义。因为我们从numpy second导入,它的日志会覆盖(或“阴影”)我们从math导入的日志变量。
一个很好的折衷方案是只从每个模块导入我们需要的特定内容:
from math import log, pi
from numpy import asarray
Submodules
我们已经看到模块包含可以引用函数或值的变量。需要注意的是,它们也可以有引用其他模块的变量。
import numpy
print("numpy.random is a", type(numpy.random))
print("it contains names such as...",dir(numpy.random)[-15:])
numpy.random is a <class 'module'> it contains names such as... ['seed', 'set_state', 'shuffle', 'standard_cauchy', 'standard_exponential', 'standard_gamma', 'standard_normal', 'standard_t', 'test', 'triangular', 'uniform', 'vonmises', 'wald', 'weibull', 'zipf']
因此,如果我们如上所述导入numpy,那么调用随机“子模块”中的函数将需要两个点。
# Roll 10 dice
rolls = numpy.random.randint(low=1, high=6, size=10)
rolls
array([3, 4, 3, 4, 5, 5, 2, 1, 3, 3])
Oh the places you'll go, oh the objects you'll see
所以,在6节课之后,你是一个擅长int、float、bools、list、string和dicts的人(对吧?)。
即使这是真的,也不会就此结束。当你使用各种库来完成专门的任务时,你会发现它们定义了自己的类型,你必须学会使用它们。例如,如果您使用图形库matplotlib,您将接触到它定义的表示子图、图形、标记和注释的对象。pandas函数将为您提供DataFrames和Series。
在本节中,我想与大家分享一个与奇怪类型打交道的快速生存指南。
Three tools for understanding strange objects
在上面的单元格中,我们看到调用numpy函数会给我们一个“数组”。我们以前从未见过这样的事情(无论如何,在这门课上都没有)。但不要惊慌:我们有三个熟悉的内置函数来帮助我们。
1:type()(这是什么?)
type(rolls)
numpy.ndarray
2:dir()(我能用它做什么?)
print(dir(rolls))
['T', '__abs__', '__add__', '__and__', '__array__', '__array_finalize__', '__array_function__', '__array_interface__', '__array_prepare__', '__array_priority__', '__array_struct__', '__array_ufunc__', '__array_wrap__', '__bool__', '__class__', '__complex__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__iand__', '__ifloordiv__', '__ilshift__', '__imatmul__', '__imod__', '__imul__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__ior__', '__ipow__', '__irshift__', '__isub__', '__iter__', '__itruediv__', '__ixor__', '__le__', '__len__', '__lshift__', '__lt__', '__matmul__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmatmul__', '__rmod__', '__rmul__', '__ror__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__setitem__', '__setstate__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__xor__', 'all', 'any', 'argmax', 'argmin', 'argpartition', 'argsort', 'astype', 'base', 'byteswap', 'choose', 'clip', 'compress', 'conj', 'conjugate', 'copy', 'ctypes', 'cumprod', 'cumsum', 'data', 'diagonal', 'dot', 'dtype', 'dump', 'dumps', 'fill', 'flags', 'flat', 'flatten', 'getfield', 'imag', 'item', 'itemset', 'itemsize', 'max', 'mean', 'min', 'nbytes', 'ndim', 'newbyteorder', 'nonzero', 'partition', 'prod', 'ptp', 'put', 'ravel', 'real', 'repeat', 'reshape', 'resize', 'round', 'searchsorted', 'setfield', 'setflags', 'shape', 'size', 'sort', 'squeeze', 'std', 'strides', 'sum', 'swapaxes', 'take', 'tobytes', 'tofile', 'tolist', 'tostring', 'trace', 'transpose', 'var', 'view']
# If I want the average roll, the "mean" method looks promising...
rolls.mean()
3.3
# Or maybe I just want to turn the array into a list, in which case I can use "tolist"
rolls.tolist()
[3, 4, 3, 4, 5, 5, 2, 1, 3, 3]
3:help()(告诉我更多)
# That "ravel" attribute sounds interesting. I'm a big classical music fan.
help(rolls.ravel)
Help on built-in function ravel:ravel(...) method of numpy.ndarray instancea.ravel([order])Return a flattened array.Refer to `numpy.ravel` for full documentation.See Also--------numpy.ravel : equivalent functionndarray.flat : a flat iterator on the array.
# Okay, just tell me everything there is to know about numpy.ndarray
# (Click the "output" button to see the novel-length output)
help(rolls)
(当然,您也可能更喜欢查看在线文档。)
Operator overloading
下面表达式的值是多少?
[3, 4, 1, 2, 2, 1] + 10
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /tmp/ipykernel_19/2144087748.py in <module> ----> 1 [3, 4, 1, 2, 2, 1] + 10TypeError: can only concatenate list (not "int") to list
多么愚蠢的问题。当然,这是一个错误。
但是。。。
rolls + 10
array([13, 14, 13, 14, 15, 15, 12, 11, 13, 13])
我们可能认为Python严格控制其核心语法的行为,例如+、<、in、==或方括号,用于索引和切片。但事实上,这是一种非常不干涉的方法。当你定义一个新类型时,你可以选择加法是如何工作的,或者对该类型的对象与其他对象相等意味着什么。
列表的设计者决定不允许将它们添加到数字中。numpy数组的设计者采取了不同的方式(将数字添加到数组的每个元素中)。
以下是numpy数组如何与Python运算符意外交互(或至少与列表不同)的几个示例。
# At which indices are the dice less than or equal to 3?
rolls <= 3
array([ True, False, True, False, False, False, True, True, True,True])
xlist = [[1,2,3],[2,4,6],]
# Create a 2-dimensional array
x = numpy.asarray(xlist)
print("xlist = {}\nx =\n{}".format(xlist, x))
xlist = [[1, 2, 3], [2, 4, 6]] x = [[1 2 3][2 4 6]]
# Get the last element of the second row of our numpy array
x[1,-1]
6
# Get the last element of the second sublist of our nested list?
xlist[1,-1]
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) /tmp/ipykernel_19/3020169379.py in <module>1 # Get the last element of the second sublist of our nested list? ----> 2 xlist[1,-1]TypeError: list indices must be integers or slices, not tuple
numpy的ndarray类型专门用于处理多维数据,因此它定义了自己的索引逻辑,允许我们通过元组进行索引,以指定每个维度的索引。
When does 1 + 1 not equal 2?
事情可能会变得比这更奇怪。你可能听说过(甚至使用过)tensorflow,一个广泛用于深度学习的Python库。它广泛使用运算符重载。
import tensorflow as tf
# Create two constants, each with value 1
a = tf.constant(1)
b = tf.constant(1)
# Add them together to get...
a + b
<tf.Tensor: shape=(), dtype=int32, numpy=2>
a+b不是2,它是(引用tensorflow的文档)。。。
操作输出之一的符号句柄。它不保存该操作输出的值,而是提供了一种在TensorFlow tf.Session中计算这些值的方法。
重要的是要意识到这类事情是可能的,库通常会以不明显或看似神奇的方式使用运算符重载。
理解Python运算符在应用于int、string和list时的工作原理并不能保证你能够立即理解它们在应用于tensorflow Tensor、numpy-ndarray或pandas DataFrame时的作用。
例如,一旦你对DataFrames有了一点了解,下面这样的表达式就开始看起来非常直观:
# Get the rows with population over 1m in South America
df[(df['population'] > 10**6) & (df['continent'] == 'South America')]
但为什么它有效呢?上面的例子有5个不同的重载运算符。这些操作中的每一个都在做什么?当事情开始出错时,知道答案会有所帮助。
Curious how it all works?
你是否曾经对一个对象调用help()或dir(),并想知道那些带双下划线的名字到底是什么?
print(dir(list))
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
事实证明,这与操作员过载直接相关。
当Python程序员想要定义运算符在其类型上的行为时,他们通过实现以2个下划线开头和结尾的特殊名称的方法来实现,例如__lt__、__setattr__或__contains__。通常,遵循这种双下划线格式的名称对Python具有特殊含义。
例如,[1,2,3]中的表达式x实际上是在幕后调用list方法__contains__。它相当于(更丑的)[1,2,3]__包含_(x)。
如果你想了解更多,可以查看Python的官方文档,其中描述了许多、更多的这些特殊的“下划线”方法。
在这些课程中,我们不会定义自己的类型(如果有时间的话!),但我希望你以后能体验到定义自己精彩、奇怪的类型的乐趣。
Your turn!
转到最后的编码练习,再回答一轮编码问题,涉及导入、使用不熟悉的对象以及更多的编码问题。