{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "##内容索引\n", "1. 通用函数\n", " - 创建通用函数 --- frompyfunc工厂函数\n", " - 通用函数的方法 --- reduce函数、accumulate函数、reduceat函数、outer函数\n", "2. 数组的除法运算 --- divide函数、true_divide函数、floor_divide函数\n", "3. 数组的模运算 --- mod函数、remainder函数、fmod函数\n", "4. 位操作函数和比较函数 --- " ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##1. 通用函数\n", "通用函数的输入时一组标量、输出也是一组标量,他们通常可以对应于基本数学运算。如加减乘除。\n", "\n", "[通用函数的文档](http://docs.scipy.org/doc/numpy/reference/ufuncs.html#ufunc)\n", "\n", "通用函数是对普通的python函数进行矢量化,它是对ndarray对象的逐个元素的操作。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###1.1 创建通用函数\n", "使用NumPy中的frompyfunc函数,通过一个Python函数来创建通用函数。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "# 定义一个Python函数\n", "def pyFunc(a):\n", " result = np.zeros_like(a)\n", " # 这里可以看出来对逐个元素的操作\n", " result = 42\n", " return result" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用zeros_like函数创建一个和a形状相同的、元素全部为0的数组result。flat属性提供了一个扁平迭代器,可以逐个设置数组元素的值。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "使用frompyfunc创建通用函数,制定输入参数为1,输出参数为1。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The answer:\n", "[42 42 42 42]\n" ] } ], "source": [ "ufunc1 = np.frompyfunc(pyFunc, 1, 1)\n", "ret = ufunc1(np.arange(4))\n", "print \"The answer:\\n\", ret" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The answer:\n", "[[42 42]\n", " [42 42]]\n" ] } ], "source": [ "ret = ufunc1(np.arange(4).reshape(2,2))\n", "print \"The answer:\\n\", ret" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "**使用在第五节介绍的vectorize函数**" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The answer:\n", "[42 42 42 42]\n" ] } ], "source": [ "func2 = np.vectorize(pyFunc)\n", "ret = func2(np.arange(4))\n", "print \"The answer:\\n\", ret" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###1.2 通用函数的方法\n", "通用函数并不是真正的函数,而是能够表示函数的numpy.ufunc的对象。frompyfunc是一个构造ufunc类对象的工厂函数。\n", "\n", "通用函数类有4个方法:**reduce、accumulate、reduceat、outer。这些方法只对输入两个参数、输出一个参数的ufunc对象有效**。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###1.3 在add函数上分别调用4个方法" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "####(1) reduce方法:沿着指定的轴,在连续的数组元素之间递归调用通用函数,即可得到输入数组的规约(reduce)计算结果。对于add函数,其对数组的reduce计算结果等价于对数组元素求和。" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:\n", "[0 1 2 3 4 5 6 7 8]\n", "Reduce:\n", "36\n" ] } ], "source": [ "a = np.arange(9)\n", "print \"a:\\n\", a\n", "print \"Reduce:\\n\", np.add.reduce(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "####(2) accumulate方法:可以递归作用于输入数组,与reduce不同的是,它将存储运算的中间结果并返回。add函数上调用accumulate方法,等价于直接调用cumsum函数。" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Accumulate:\n", "[ 0 1 3 6 10 15 21 28 36]\n" ] } ], "source": [ "print \"Accumulate:\\n\", np.add.accumulate(a)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "cumsum:\n", "[ 0 1 3 6 10 15 21 28 36]\n" ] } ], "source": [ "print \"cumsum:\\n\", np.cumsum(a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "####(3) reduceat方法有点复杂,它需要输入一个数组以及一个索引值列表作为参数" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Reduceat:\n", "[10 5 20 15]\n" ] } ], "source": [ "print \"Reduceat:\\n\", np.add.reduceat(a, [0,5,2,7])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "- 第一步:用到索引值列表中的0和5,实际上就是对数组中索引值在0到5之间的元素进行reduce操作\n", "- 第二步:用到索引值5和2.由于2比5小,所以直接返回索引值为5的元素\n", "- 第三步:用到索引值2和7,计算2到7的数组的reduce操作\n", "- 第四步:用到索引值7,对索引值7开始直到数组末尾的元素进行reduce操作" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Reduceat step 1: 10\n", "Reduceat step 2: 5\n", "Reduceat step 3: 20\n", "Reduceat step 4: 15\n" ] } ], "source": [ "print \"Reduceat step 1:\", np.add.reduce(a[0:5])\n", "print \"Reduceat step 2:\", a[5]\n", "print \"Reduceat step 3:\", np.add.reduce(a[2:7])\n", "print \"Reduceat step 4:\", np.add.reduce(a[7:])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "####(4) outer方法:返回一个数组,它的秩(rank)等于两个输入数组的秩的和。它会作用于两个输入数组之间存在的所有元素对。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Outer:\n", "[[ 0 1 2 3 4 5 6 7 8]\n", " [ 1 2 3 4 5 6 7 8 9]\n", " [ 2 3 4 5 6 7 8 9 10]]\n" ] } ], "source": [ "print \"Outer:\\n\", np.add.outer(np.arange(3), a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##2. 数组的除法运算" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "在NumPy中,计算算术运算符+、-、* 隐式关联着通用函数add、subtrack和multiply。也就是说,当你对NumPy数组使用这些运算符时,对应的通用函数将自动被调用。\n", "\n", "除法包含的过程比较复杂,在数组的除法运算中射击三个通用函数divide、true_divide和floor_division,以及两个对应的运算符/和//。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###(1) divide函数在整数除法中均只保留整数部分" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Divide:\n", "[2 3 1] [0 0 0]\n" ] } ], "source": [ "a = np.array([2, 6, 5])\n", "b = np.array([1, 2, 3])\n", "print \"Divide:\\n\", np.divide(a, b), np.divide(b, a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "运算结果的小数部分被截断了" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Divide:\n", "[ 2.1 3.1 2.63157895] [ 0.47619048 0.32258065 0.38 ]\n" ] } ], "source": [ "c = np.array([2.1, 6.2, 5.0])\n", "d = np.array([1, 2, 1.9])\n", "print \"Divide:\\n\", np.divide(c, d), np.divide(d, c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "divide函数如果有一方是浮点数,那么结果也是浮点数结果" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###(2) true_divide函数与数学中的除法定义更为接近,返回除法的浮点数结果不截断" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "True Divide:\n", "[ 2. 3. 1.66666667] [ 0.5 0.33333333 0.6 ]\n" ] } ], "source": [ "print \"True Divide:\\n\", np.true_divide(a, b), np.true_divide(b, a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###(3) floor_divide函数总是返回整数结果,相当于先调用divide函数再调用floor函数。\n", "floor函数对浮点数进行**向下取整**并返回整数。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Floor Divide:\n", "[2 3 1] [0 0 0]\n" ] } ], "source": [ "print \"Floor Divide:\\n\", np.floor_divide(a, b), np.floor_divide(b, a)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**默认情况下,使用/运算符相当于调用divide函数,使用//运算符对应于floor_divide函数**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##3. 数组的模运算\n", "计算模数或者余数,可以使用NumPy中的mod、remainder和fmod函数。也可以用%运算符。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###(1) remainder函数逐个返回两个数组中元素相除后的余数,如果第二个数字为0,则直接返回0" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a:\n", "[-4 -3 -2 -1 0 1 2 3]\n", "Remainder:\n", "[0 1 0 1 0 1 0 1]\n" ] } ], "source": [ "a = np.arange(-4,4)\n", "print \"a:\\n\", a\n", "print \"Remainder:\\n\", np.remainder(a, 2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**mod函数与remainder函数的功能完全一致,%操作符仅仅是remainder函数的简写**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###(2) fmod函数处理负数的方式和remainder不同。所得余数的正负由被除数决定,与除数的正负无关" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Fmod:\n", "[ 0 -1 0 -1 0 1 0 1]\n" ] } ], "source": [ "print \"Fmod:\\n\", np.fmod(a, 2)" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0 -1 0 -1 0 1 0 1]\n" ] } ], "source": [ "print np.fmod(a, -2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "##4. 位操作函数和比较函数\n", "位操作函数可以在整数或整数数组的位上进行操作,它们都是通用函数。\n", "\n", "位操作符:^、&、|、<<、>>等。\n", "\n", "比较操作符:<、>、==等。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###4.1 检查两个整数的符号是否一致\n", "这里要用到XOR或者^操作符。XOR操作符又称为**不等运算符**,因此当两个操作数的符号不一致时,XOR操作的结果为负数。\n", "\n", "在NumPy中,^操作符对应于bitwise_xor函数,<操作符对应于less函数。" ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Sign different? [ True True True True True True True True True False True True\n", " True True True True True True]\n", "Sign different? [ True True True True True True True True True False True True\n", " True True True True True True]\n" ] } ], "source": [ "x = np.arange(-9, 9)\n", "y = -x\n", "print \"Sign different? \", (x^y) < 0\n", "print \"Sign different? \", np.less(np.bitwise_xor(x, y), 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "除了等于0的情况,所有整数对的符号都不一样。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###4.2 检查一个数是否为2的幂数\n", "在二进制数中,2的幂数表示为一个1后面跟着一串0的形式。**如果在2的幂数以及比它小1的数之间进行位与操作AND,那么应该等于0。**\n", "\n", "在NumPy中,&操作符对应于bitwise_and函数,==操作符对应于equal函数。" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]\n", "Power of 2 ?\n", "[ True True True False True False False False True False False False\n", " False False False False True False False False]\n", "Power of 2 ?\n", "[ True True True False True False False False True False False False\n", " False False False False True False False False]\n" ] } ], "source": [ "b = np.arange(20)\n", "print b\n", "print \"Power of 2 ?\\n\", (b & (b-1)) == 0\n", "print \"Power of 2 ?\\n\", np.equal(np.bitwise_and(b, (b-1)), 0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "###4.3 计算一个数被2的幂数整除后的余数\n", "计算余数的技巧只在模为2的幂数时有效。二进制的位左移一位,数值翻倍。\n", "\n", "**上一个例子看到,将2的幂数减去1,得到一串1组成的二进制数,这为我们提供了掩码,与这样的掩码做位与操作,即可得到以2的幂数作为模的余数。**\n", "\n", "在NumPy中,<<操作符对应于left_shift函数。" ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Modulus 4:\n", "[3 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 0]\n" ] } ], "source": [ "print \"Modulus 4:\\n\", x & ((1<<2) - 1)" ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def mod_2_pow(x, n):\n", " mod = x & ((1<