SegmentFault 阿尔法的Python笔记最新的文章
2019-07-22T16:13:14+08:00
https://segmentfault.com/feeds/blogs
https://creativecommons.org/licenses/by-nc-nd/4.0/
Python 数据科学常用包 (一) Numpy
https://segmentfault.com/a/1190000019836570
2019-07-22T16:13:14+08:00
2019-07-22T16:13:14+08:00
alpha94511
https://segmentfault.com/u/alpha94511
0
<h2>NumPy是什么?</h2>
<p>今天开始会陆续为大家带来数据科学常用包的基础用法</p>
<p>数据分析的工作涉及到大量的数值运算,一个高效方便的科学计算工具是必不可少的。Python语言一开始并不是设计为科学计算使用的语言,随着越来越多的人发现Python的易用性,逐渐出现了关于Python的大量外部扩展,Numpy (Numeric Python)就是其中之一。</p>
<p>Numpy提供了大量的数值编程工具,可以方便地处理向量、矩阵等运算,极大地便利了人们在科学计算方面的工作。另一方面,Python是免费,相比于花费高额的费用使用Matlab,Numpy的出现使Python得到了更多人的青睐。</p>
<p>我们可以简单看一下如何开始使用NumPy:</p>
<pre><code class="python">import numpy as np
numpy.version.full_version</code></pre>
<pre><code>'1.16.4'
</code></pre>
<h2>二、NumPy对象:数组</h2>
<p>NumPy中的基本对象是同类型的多维数组(homogeneous multidimensional array),这和C++中的数组是一致的,例如字符型和数值型就不可共存于同一个数组中。先上例子:</p>
<pre><code class="python">a = np.arange(20)
print(a)</code></pre>
<pre><code>[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
</code></pre>
<p>这里我们生成了一个一维数组a,从0开始,步长为1,长度为20。Python中的计数是从0开始的,R和Matlab的使用者需要小心。</p>
<p>我们可以通过"type"函数查看a的类型,这里显示a是一个array:</p>
<pre><code class="python">type(a)</code></pre>
<pre><code>numpy.ndarray
</code></pre>
<p>通过函数"reshape",我们可以重新构造一下这个数组,例如,我们可以构造一个4*5的二维数组,其中"reshape"的参数表示各维度的大小,且按各维顺序排列(两维时就是按行排列,这和R中按列是不同的):</p>
<pre><code class="python">a = a.reshape(4, 5)
print(a)</code></pre>
<pre><code>[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
</code></pre>
<p>构造更高维的也没问题:</p>
<pre><code class="python">a = a.reshape(2, 2, 5)
print(a)</code></pre>
<pre><code>[[[ 0 1 2 3 4]
[ 5 6 7 8 9]]
[[10 11 12 13 14]
[15 16 17 18 19]]]
</code></pre>
<p>既然a是array,我们还可以调用array的函数进一步查看a的相关属性:"ndim"查看维度;"shape"查看各维度的大小;"size"查看全部的元素个数,等于各维度大小的乘积;"dtype"可查看元素类型;"dsize"查看元素占位(bytes)大小。</p>
<pre><code class="python">a.ndim</code></pre>
<pre><code>3
</code></pre>
<pre><code class="python">a.shape</code></pre>
<pre><code>(2, 2, 5)
</code></pre>
<pre><code class="python">a.size</code></pre>
<pre><code>20
</code></pre>
<pre><code class="python">a.dtype</code></pre>
<pre><code>dtype('int32')
</code></pre>
<h2>三、创建数组</h2>
<p>数组的创建可通过转换列表实现,高维数组可通过转换嵌套列表实现:</p>
<pre><code class="python">raw = [0,1,2,3,4]
a = np.array(raw)
a</code></pre>
<pre><code>array([0, 1, 2, 3, 4])
</code></pre>
<pre><code class="python">raw = [[0,1,2,3,4], [5,6,7,8,9]]
b = np.array(raw)
b</code></pre>
<pre><code>array([[0, 1, 2, 3, 4],
[5, 6, 7, 8, 9]])
</code></pre>
<p>一些特殊的数组有特别定制的命令生成,如4*5的全零矩阵:</p>
<pre><code class="python">d = (4, 5)
np.zeros(d)</code></pre>
<pre><code>array([[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0.]])
</code></pre>
<p>默认生成的类型是浮点型,可以通过指定类型改为整型:</p>
<pre><code class="python">d = (4, 5)
np.ones(d, dtype=int)</code></pre>
<pre><code>array([[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1]])
</code></pre>
<p>[0, 1)区间的随机数数组:</p>
<pre><code class="python">np.random.rand(5)</code></pre>
<pre><code>array([0.80378557, 0.09833667, 0.95280995, 0.17707594, 0.80651926])
</code></pre>
<p>服从正态分布的随机数组:</p>
<pre><code class="python">np.random.randn(5)</code></pre>
<pre><code>array([ 0.678737 , -1.14965615, -1.40492579, 1.22479651, 0.2751816 ])
</code></pre>
<h2>四、数组操作</h2>
<p>简单的四则运算已经重载过了,全部的'+','-','*','/'运算都是基于全部的数组元素的,以加法为例:</p>
<pre><code class="python">a = np.array([[1.0, 2], [2, 4]])
print ("a:\n",a)
b = np.array([[3.2, 1.5], [2.5, 4]])
print ("b:\n",b)
print ("a+b:\n",a+b)</code></pre>
<pre><code>a:
[[1. 2.]
[2. 4.]]
b:
[[3.2 1.5]
[2.5 4. ]]
a+b:
[[4.2 3.5]
[4.5 8. ]]
</code></pre>
<p>这里可以发现,a中虽然仅有一个与元素是浮点数,其余均为整数,在处理中Python会自动将整数转换为浮点数(因为数组是同质的),并且,两个二维数组相加要求各维度大小相同。当然,NumPy里这些运算符也可以对标量和数组操作,结果是数组的全部元素对应这个标量进行运算,还是一个数组:</p>
<pre><code class="python">print ("3 * a \n",3*a)
print ("b + 1.8 \n",b )</code></pre>
<pre><code>3 * a :
[[ 3. 6.]
[ 6. 12.]]
b + 1.8
[[3.2 1.5]
[2.5 4. ]]
</code></pre>
<p>类似C++,'+='、'-='、'*='、'/='操作符在NumPy中同样支持:</p>
<pre><code class="python">a /= 2
a</code></pre>
<pre><code>array([[0.5, 1. ],
[1. , 2. ]])
</code></pre>
<p>开根号求指数也很容易:</p>
<pre><code class="python">print(a)</code></pre>
<pre><code>[[0.5 1. ]
[1. 2. ]]
</code></pre>
<pre><code class="python">print ("np.exp:\n",np.exp(a))</code></pre>
<pre><code>np.exp:
[[1.64872127 2.71828183]
[2.71828183 7.3890561 ]]
</code></pre>
<pre><code class="python">print ("np.sqrt:\n",np.sqrt(a))</code></pre>
<pre><code>np.sqrt:
[[0.70710678 1. ]
[1. 1.41421356]]
</code></pre>
<pre><code class="python">print ("np.square:\n",np.square(a))</code></pre>
<pre><code>np.square:
[[0.25 1. ]
[1. 4. ]]
</code></pre>
<pre><code class="python">print ("np.power:\n",np.power(a,3))</code></pre>
<pre><code>np.power:
[[0.125 1. ]
[1. 8. ]]
</code></pre>
<p>需要知道二维数组的最大最小值怎么办?想计算全部元素的和、按行求和、按列求和怎么办?NumPy的ndarray类已经做好函数了:</p>
<pre><code class="python">a = np.arange(20).reshape(4,5)
a</code></pre>
<pre><code>array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19]])
</code></pre>
<pre><code class="python">print("sum of all elements in a: " + str(a.sum()))
print("maximum element in a: " + str(a.max()))
print("minimum element in a: " + str(a.min()))
print("maximum element in each row of a: " + str(a.max(axis=1)))
print("minimum element in each column of a: " + str(a.min(axis=0)))</code></pre>
<pre><code>sum of all elements in a: 190
maximum element in a: 19
minimum element in a: 0
maximum element in each row of a: [ 4 9 14 19]
minimum element in each column of a: [0 1 2 3 4]
</code></pre>
<p>科学计算中大量使用到矩阵运算,除了数组,NumPy同时提供了矩阵对象(matrix)。矩阵对象和数组的主要有两点差别:一是矩阵是二维的,而数组的可以是任意正整数维;二是矩阵的'*'操作符进行的是矩阵乘法,乘号左侧的矩阵列和乘号右侧的矩阵行要相等,而在数组中'*'操作符进行的是每一元素的对应相乘,乘号两侧的数组每一维大小需要一致。数组可以通过asmatrix或者mat转换为矩阵,或者直接生成也可以:</p>
<pre><code class="python">a = np.arange(20).reshape(4, 5)
a = np.asmatrix(a)
print(type(a))
b = np.matrix('1.0 2.0; 3.0 4.0')
print(type(b))</code></pre>
<pre><code><class 'numpy.matrix'>
<class 'numpy.matrix'>
</code></pre>
<p>再来看一下矩阵的乘法,这使用arange生成另一个矩阵b,arange函数还可以通过arange(起始,终止,步长)的方式调用生成等差数列,注意含头不含尾。</p>
<pre><code class="python">b = np.arange(2, 45, 3).reshape(5, 3)
b = np.mat(b)
print(b)</code></pre>
<pre><code>[[ 2 5 8]
[11 14 17]
[20 23 26]
[29 32 35]
[38 41 44]]
</code></pre>
<p>回到我们的问题,矩阵a和b做矩阵乘法:</p>
<pre><code class="python">print ("matrix a:\n",a)
print("matrix b:\n",b)
c = a * b
print("matrix c:\n",c)</code></pre>
<pre><code>matrix a:
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
matrix b:
[[ 2 5 8]
[11 14 17]
[20 23 26]
[29 32 35]
[38 41 44]]
matrix c:
[[ 290 320 350]
[ 790 895 1000]
[1290 1470 1650]
[1790 2045 2300]]
</code></pre>
<h2>五、数组元素访问</h2>
<p>数组和矩阵元素的访问可通过下标进行,以下均以二维数组(或矩阵)为例:</p>
<pre><code class="python">a = np.array([[3.2, 1.5], [2.5, 4]])
print(a[0][1])
print(a[0, 1])</code></pre>
<pre><code>1.5
1.5
</code></pre>
<p>可以通过下标访问来修改数组元素的值:</p>
<pre><code class="python">b = a
a[0][1] = 2.0
print(a)
print(b)</code></pre>
<pre><code>[[3.2 2. ]
[2.5 4. ]]
[[3.2 2. ]
[2.5 4. ]]
</code></pre>
<p>现在问题来了,明明改的是a0,怎么连b0也跟着变了?这个陷阱在Python编程中很容易碰上,其原因在于Python不是真正将a复制一份给b,而是将b指到了a对应数据的内存地址上。想要真正的复制一份a给b,可以使用copy:</p>
<pre><code class="python">a = np.array([[3.2, 1.5], [2.5, 4]])
b = a.copy()
a[0][1] = 2.0
print ("a:",a)
print ("b:",b)
</code></pre>
<pre><code>a: [[3.2 2. ]
[2.5 4. ]]
b: [[3.2 1.5]
[2.5 4. ]]
</code></pre>
<p>若对a重新赋值,即将a指到其他地址上,b仍在原来的地址上:</p>
<pre><code class="python">a = np.array([[3.2, 1.5], [2.5, 4]])
b = a
a = np.array([[2, 1], [9, 3]])
print ("a:\n",a)
print ("b:\n",b)</code></pre>
<pre><code>a:
[[2 1]
[9 3]]
b:
[[3.2 1.5]
[2.5 4. ]]
</code></pre>
<p>利用':'可以访问到某一维的全部数据,例如取矩阵中的指定列:</p>
<pre><code class="python">a = np.arange(20).reshape(4, 5)
print ("a:\n",a)
print ("the 2nd and 4th column of a::\n",a[:,[1,3]])</code></pre>
<pre><code>a:
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
the 2nd and 4th column of a::
[[ 1 3]
[ 6 8]
[11 13]
[16 18]]
</code></pre>
<p>稍微复杂一些,我们尝试取出满足某些条件的元素,这在数据的处理中十分常见,通常用在单行单列上。下面这个例子是将第一列大于5的元素(10和15)对应的第三列元素(12和17)取出来:</p>
<pre><code class="python">a[:, 2][a[:, 0] > 5]</code></pre>
<pre><code>array([12, 17])
</code></pre>
<p>可使用where函数查找特定值在数组中的位置:</p>
<pre><code class="python">loc = numpy.where(a==11)
print(loc)
print(a[loc[0][0], loc[1][0]])</code></pre>
<pre><code>(array([2], dtype=int64), array([1], dtype=int64))
11
</code></pre>
<h2>六、数组操作</h2>
<p>还是拿矩阵(或二维数组)作为例子,首先来看矩阵转置:</p>
<pre><code class="python">a = np.random.rand(2,4)
print ("a:\n",a)
a = np.transpose(a)
print ("a is an array, by using transpose(a):\n",a)
b = np.random.rand(2,4)
b = np.mat(b)
print ("b:\n",b)
print ("b is a matrix, by using b.T:\n",b.T)</code></pre>
<pre><code>a:
[[0.49632956 0.65061015 0.36037379 0.29664563]
[0.18319505 0.45525932 0.08422801 0.75167911]]
a is an array, by using transpose(a):
[[0.49632956 0.18319505]
[0.65061015 0.45525932]
[0.36037379 0.08422801]
[0.29664563 0.75167911]]
b:
[[0.51087064 0.2058778 0.88659661 0.78428426]
[0.62716285 0.46838085 0.63015861 0.69754748]]
b is a matrix, by using b.T:
[[0.51087064 0.62716285]
[0.2058778 0.46838085]
[0.88659661 0.63015861]
[0.78428426 0.69754748]]
</code></pre>
<p>矩阵求逆:</p>
<pre><code class="python">import numpy.linalg as nlg
a = np.random.rand(2,2)
print ("a:\n",a)
ia = nlg.inv(a)
print ("inverse of a:\n",ia)
print ("a * inv(a):\n",a * ia)</code></pre>
<pre><code>a:
[[0.7748124 0.08125528]
[0.99696367 0.73251292]]
inverse of a:
[[ 1.50551971 -0.16700242]
[-2.04904025 1.59245703]]
a * inv(a):
[[ 1.16649535 -0.01356983]
[-2.04281868 1.16649535]]
</code></pre>
<p>求特征值和特征向量</p>
<pre><code class="python">a = np.random.rand(3,3)
eig_value, eig_vector = nlg.eig(a)
print ("eigen value:\n",eig_value)
print ("eigen vector:\n",eig_vector)</code></pre>
<pre><code>eigen value:
[ 1.75590394+0.j -0.25188941+0.08867887j -0.25188941-0.08867887j]
eigen vector:
[[ 0.33976986+0.j 0.47679494-0.21597791j 0.47679494+0.21597791j]
[ 0.81509742+0.j 0.24255425+0.21077809j 0.24255425-0.21077809j]
[ 0.46922557+0.j -0.78915154+0.j -0.78915154-0.j ]]
</code></pre>
<p>按列拼接两个向量成一个矩阵:</p>
<pre><code class="python">a = np.array((1,2,3))
b = np.array((2,3,4))
print(np.column_stack((a,b)))</code></pre>
<pre><code>[[1 2]
[2 3]
[3 4]]
</code></pre>
<p>在循环处理某些数据得到结果后,将结果拼接成一个矩阵是十分有用的,可以通过vstack和hstack完成:</p>
<pre><code class="python">a = np.random.rand(2,2)
b = np.random.rand(2,2)
print ("a:\n",a)
print ("b:\n",b)
c = np.hstack([a,b])
d = np.vstack([a,b])
print("horizontal stacking a and b:\n",c)
print("vertical stacking a and b:\n",d)</code></pre>
<pre><code>a:
[[0.50331973 0.49651025]
[0.89325327 0.31245265]]
b:
[[0.35846554 0.56841584]
[0.88041789 0.81287829]]
horizontal stacking a and b:
[[0.50331973 0.49651025 0.35846554 0.56841584]
[0.89325327 0.31245265 0.88041789 0.81287829]]
vertical stacking a and b:
[[0.50331973 0.49651025]
[0.89325327 0.31245265]
[0.35846554 0.56841584]
[0.88041789 0.81287829]]
</code></pre>
<h2>七、缺失值</h2>
<p>缺失值在分析中也是信息的一种,NumPy提供nan作为缺失值的记录,通过isnan判定。</p>
<pre><code class="python">a = np.random.rand(2,2)
a[0, 1] = np.nan
print(np.isnan(a))</code></pre>
<pre><code>[[False True]
[False False]]
</code></pre>
<p>nan_to_num可用来将nan替换成0,pandas中提供能指定nan替换值的函数。</p>
<pre><code class="python">print(np.nan_to_num(a))</code></pre>
<pre><code>[[0.04279427 0. ]
[0.08386045 0.3567586 ]]
</code></pre>
<h2>参考文献</h2>
<ol>
<li><a href="https://link.segmentfault.com/?enc=VrEZkoR8gMMQdKxIkwhz1Q%3D%3D.pFRQDnfNdVBEQOJCFLsDZ0eHE4qBNJoi9%2FeZXjcmvbmHQLypnzxE%2FPIctn8IfNt8" rel="nofollow">http://wiki.scipy.org/Tentati...</a></li>
<li>Sheppard K. Introduction to Python for econometrics, statistics and data analysis. Self-published, University of Oxford, version, 2012, 2.</li>
</ol>
Python 面向对象编程OOP (五) 写类神器:Dataclass
https://segmentfault.com/a/1190000019822495
2019-07-20T17:25:03+08:00
2019-07-20T17:25:03+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>Dataclasses</h2>
<p>大家好,上一期受到了朋友的启发,这一期我主要记录一下我的Dataclasses的学习过程。</p>
<p>之前简单回顾了attrs的用法,这一期来看更简洁的自带写类神器:dataclasses,需要注意,版本要大于等于Python 3.7</p>
<p>官方文档链接:<a href="https://link.segmentfault.com/?enc=S%2BOsler3DIOfLVxWRNdhpg%3D%3D.dxW8NN5xjn0o%2B7UbEe2Ecch93mICnFh7Lf8ojasX3yRuWx1FVK9IL%2FfOjtdwTeO4%2Bqh993EpY7ux5SVOCo8A%2Fg%3D%3D" rel="nofollow"> Data Classes</a><br>下面直接来看例子:</p>
<h2>创建Dataclass</h2>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class Position:
name: str
lon: float
lat: float
</code></pre>
<p>可以发现,主要起作用的是装饰符@dataclass ,需要注意,如果想要使用dataclass,需要Python 3.7或更高版本<br>使用dataclass的好处是可以节省书写__init()__等一些常用的实例方法</p>
<p>这里创建一个Position类,用来显示一个地点的位置</p>
<ul>
<li>name:地点的名字</li>
<li>lon:经度</li>
<li>lat:纬度</li>
</ul>
<p>新建一个实例来看看:</p>
<pre><code class="python">>>> pos = Position('Oslo', 10.8, 59.9)
>>> print(pos)
Position(name='Oslo', lon=10.8, lat=59.9)
>>> pos.lat
59.9
>>> print(f'{pos.name} is at {pos.lat}°N, {pos.lon}°E')
Oslo is at 59.9°N, 10.8°E</code></pre>
<p>除了这种方法,还要一种类似创建namedtuple的方式也可以:</p>
<pre><code class="python">
from dataclasses import make_dataclass
Position = make_dataclass('Position', ['name', 'lat', 'lon'])</code></pre>
<h2>默认值</h2>
<p>让我们看看如何给类的属性添加默认值:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class Position:
name: str
lon: float = 0.0
lat: float = 0.0</code></pre>
<p>效果和普通的类设定初始值的效果是一样的:</p>
<pre><code class="python">>>> Position('Null Island')
Position(name='Null Island', lon=0.0, lat=0.0)
>>> Position('Greenwich', lat=51.8)
Position(name='Greenwich', lon=0.0, lat=51.8)
>>> Position('Vancouver', -123.1, 49.3)
Position(name='Vancouver', lon=-123.1, lat=49.3)
</code></pre>
<h2>输入提示</h2>
<p>大家可以发现我们的Positon类规定了三个属性的类型:</p>
<ul>
<li>name:str</li>
<li>lon:float</li>
<li>lat:float</li>
</ul>
<p>现在如果想要开放限制,允许任意的数据类型,可以这么做:</p>
<pre><code class="python">from dataclasses import dataclass
from typing import Any
@dataclass
class WithoutExplicitTypes:
name: Any
value: Any = 42</code></pre>
<p>这样运行的时候不会报错,哪怕瞎传参:</p>
<pre><code class="python">>>> Position(3.14, 'pi day', 2018)
Position(name=3.14, lon='pi day', lat=2018)</code></pre>
<h2>添加一个方法</h2>
<p>现在我们想要计算两个地点的距离,可以参考如下公式:<br><img src="/img/remote/1460000019822498?w=551&h=38" alt="The haversine formula" title="The haversine formula"></p>
<p>根据这个公式为类添加一个<code>.distance_to()</code>方法</p>
<pre><code class="python">from dataclasses import dataclass
from math import asin, cos, radians, sin, sqrt
@dataclass
class Position:
name: str
lon: float = 0.0
lat: float = 0.0
def distance_to(self, other):
r = 6371 # Earth radius in kilometers
lam_1, lam_2 = radians(self.lon), radians(other.lon)
phi_1, phi_2 = radians(self.lat), radians(other.lat)
h = (sin((phi_2 - phi_1) / 2)**2
+ cos(phi_1) * cos(phi_2) * sin((lam_2 - lam_1) / 2)**2)
return 2 * r * asin(sqrt(h))</code></pre>
<p>实验一下:</p>
<pre><code class="python">>>> oslo = Position('Oslo', 10.8, 59.9)
>>> vancouver = Position('Vancouver', -123.1, 49.3)
>>> oslo.distance_to(vancouver)
7181.7841229421165</code></pre>
<h2>更加灵活的应用</h2>
<p>目前为止我们已经看到了dataclass的基础用法,现在我们看看根据实际需要,有哪些其他灵活的应用方式。</p>
<p>现在创建两个类,纸牌类和牌库类,纸牌类的属性包括花色和数字,牌库是List类型,包含纸牌类的一些实例</p>
<pre><code class="python">from dataclasses import dataclass
from typing import List
@dataclass
class PlayingCard:
rank: str
suit: str
@dataclass
class Deck:
cards: List[PlayingCard]
</code></pre>
<p>现在向牌库添加红桃Q和黑桃A的操作可以这样:</p>
<pre><code class="python">>>> queen_of_hearts = PlayingCard('Q', 'Hearts')
>>> ace_of_spades = PlayingCard('A', 'Spades')
>>> two_cards = Deck([queen_of_hearts, ace_of_spades])
Deck(cards=[PlayingCard(rank='Q', suit='Hearts'),
PlayingCard(rank='A', suit='Spades')])</code></pre>
<p>现在我们可以创建一套完整的扑克牌牌库,注意这里使用了符号表示花色,建议在实际环境中改换为字符串:</p>
<pre><code>RANKS = '2 3 4 5 6 7 8 9 10 J Q K A'.split()
SUITS = '♣ ♢ ♡ ♠'.split()
def make_french_deck():
return [PlayingCard(r, s) for s in SUITS for r in RANKS]
>>> make_french_deck()
[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), ...
PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')]</code></pre>
<p>理论上,我们可以把这个方法作为Deck类的初始变量,但是根本不行,因为它可变:</p>
<pre><code>from dataclasses import dataclass
from typing import List
@dataclass
class Deck: # Will NOT work
cards: List[PlayingCard] = make_french_deck()</code></pre>
<p>面对这种情况,dataclass的解决方案是使用<code>default_factory</code>函数作为field的参数来指明:</p>
<pre><code>from dataclasses import dataclass, field
from typing import List
@dataclass
class Deck:
cards: List[PlayingCard] = field(default_factory=make_french_deck)</code></pre>
<p>现在就没有任何问题了:</p>
<pre><code>>>> Deck()
Deck(cards=[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), ...
PlayingCard(rank='K', suit='♠'), PlayingCard(rank='A', suit='♠')])</code></pre>
<h2>field</h2>
<p>现在简单总结一下dataclass中使用field涉及到的关键参数:</p>
<li><ul><li>
<code>default</code>: Default value of the field</li></ul></li>
<ul>
<li>
<code>default_factory</code>: Function that returns the initial value of the field</li>
<li>
<code>init</code>: Use field in <code>.__init__()</code> method? (Default is <code>True</code>.)</li>
<li>
<code>repr</code>: Use field in <code>repr</code> of the object? (Default is <code>True</code>.)</li>
<li>
<code>compare</code>: Include the field in comparisons? (Default is <code>True</code>.)</li>
<li>
<code>hash</code>: Include the field when calculating <code>hash()</code>? (Default is to use the same as for <code>compare</code>.)</li>
<li>
<code>metadata</code>: A mapping with information about the field</li>
</ul>
<p>最后这个metadata有点像前端h5中的那个,就是可以为类的一个属性添加一个额外的描述信息:</p>
<pre><code class="python">from dataclasses import dataclass, field
@dataclass
class Position:
name: str
lon: float = field(default=0.0, metadata={'unit': 'degrees'})
lat: float = field(default=0.0, metadata={'unit': 'degrees'})
</code></pre>
<p>可以发现,传递的是一个dict,现在可以使用fields来查看一个属性的附加信息了:</p>
<pre><code class="python">>>> from dataclasses import fields
>>> fields(Position)
(Field(name='name',type=<class 'str'>,...,metadata={}),
Field(name='lon',type=<class 'float'>,...,metadata={'unit': 'degrees'}),
Field(name='lat',type=<class 'float'>,...,metadata={'unit': 'degrees'}))
>>> lat_unit = fields(Position)[2].metadata['unit']
>>> lat_unit
'degrees'</code></pre>
<h2>描述类(str,repr)</h2>
<p>这里指的就是常用的repr(obj)和str(obj)<br>先看一下刚才的Deck()描述:</p>
<pre><code class="python">>>> Deck()
Deck(cards=[PlayingCard(rank='2', suit='♣'), PlayingCard(rank='3', suit='♣'), PlayingCard(rank='4', suit='♣'), PlayingCard(rank='5', suit='♣')...
</code></pre>
<p>有些过长了,让我们用传统的str或者repr来表达,首先从纸牌类PlayingCard开始:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class PlayingCard:
rank: str
suit: str
def __str__(self):
return f'{self.suit}{self.rank}'</code></pre>
<pre><code class="python">>>> ace_of_spades = PlayingCard('A', '♠')
>>> ace_of_spades
PlayingCard(rank='A', suit='♠')
>>> print(ace_of_spades)
♠A</code></pre>
<p>现在看上去好多了,现在再自定义下牌库类Deck的描述:</p>
<pre><code class="python">from dataclasses import dataclass, field
from typing import List
@dataclass
class Deck:
cards: List[PlayingCard] = field(default_factory=make_french_deck)
def __repr__(self):
cards = ', '.join(f'{c!s}' for c in self.cards)
return f'{self.__class__.__name__}({cards})'</code></pre>
<p>这回看上去舒服多了:</p>
<pre><code class="python">>>> Deck()
Deck(♣2, ♣3, ♣4, ♣5, ♣6, ♣7, ♣8, ♣9, ♣10, ♣J, ♣Q, ♣K, ♣A,
♢2, ♢3, ♢4, ♢5, ♢6, ♢7, ♢8, ♢9, ♢10, ♢J, ♢Q, ♢K, ♢A,
♡2, ♡3, ♡4, ♡5, ♡6, ♡7, ♡8, ♡9, ♡10, ♡J, ♡Q, ♡K, ♡A,
♠2, ♠3, ♠4, ♠5, ♠6, ♠7, ♠8, ♠9, ♠10, ♠J, ♠Q, ♠K, ♠A)
</code></pre>
<h2>不可修改的Dataclass</h2>
<p>其实这里和FrozenSet有点像,无非在装饰器中添加了一个参数frozen=True:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass(frozen=True)
class Position:
name: str
lon: float = 0.0
lat: float = 0.0</code></pre>
<p>在这样一个frozen data class中,我们不能随意赋值:</p>
<pre><code class="python">>>> pos = Position('Oslo', 10.8, 59.9)
>>> pos.name
'Oslo'
>>> pos.name = 'Stockholm'
dataclasses.FrozenInstanceError: cannot assign to field 'name'
</code></pre>
<h2>继承</h2>
<p>使用dataclass的继承也比较简单,和普通类的继承没有太大区别:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class Position:
name: str
lon: float
lat: float
@dataclass
class Capital(Position):
country: str
</code></pre>
<pre><code class="python">>>> Capital('Oslo', 10.8, 59.9, 'Norway')
Capital(name='Oslo', lon=10.8, lat=59.9, country='Norway')</code></pre>
<p>这里可以发现,创建Capital类的实例时会自动继承了父类的属性,我们只需要额外加入country这个新属性就可以了</p>
<p>假如父类初始化时有默认值:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class Position:
name: str
lon: float = 0.0
lat: float = 0.0
@dataclass
class Capital(Position):
country: str = 'Unknown'
lat: float = 40.0</code></pre>
<h2>简单优化</h2>
<p>可以用我之前提到的slot方法进行优化:</p>
<pre><code class="python">from dataclasses import dataclass
@dataclass
class SimplePosition:
name: str
lon: float
lat: float
@dataclass
class SlotPosition:
__slots__ = ['name', 'lon', 'lat']
name: str
lon: float
lat: float
</code></pre>
<h2>总结</h2>
<p>这次我记录了dataclass的基础用法,是参考了别人的文章,如果想要了解更多,还是要到官方文档去看</p>
<p>我的其他原创文章已经放到了Github上,如果感兴趣的朋友可以去看看,链接如下:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=ZwDpihOrfkKkR9MxgT8ABg%3D%3D.Rr1L3frF6Jhtsa4cxGiLjv9a19DNIY%2Fai9hfSG%2FLYovQ88mKxpHDnLY03CHEWQJ3pOQoKvrY03UuoCK787Ykcg%3D%3D" rel="nofollow">Python 精品练习题100道</a></li>
<li><a href="https://link.segmentfault.com/?enc=ymoTe1RCGEo9NjlY3oVCVQ%3D%3D.xBrYzOfV%2FS40HjoG6F1XNqLiwljR7WOOMiXRzmGPPU1EawypVPWVyUgSxfIc%2F24rCdqY4Y9%2FJPXSoMluU76fIA%3D%3D" rel="nofollow">Python 实用技巧汇总</a></li>
<li><a href="https://link.segmentfault.com/?enc=CFhL02lrlU%2F%2F0MOpsd7J2A%3D%3D.CgLxhHa2o%2FTWHhOmGhWfdNebmld4vSiVAl25jSiwN5KINX%2FXhg9VyB78UIczF%2Fp9" rel="nofollow">Python Pandas教程</a></li>
</ul>
Python 面向对象编程OOP (四) 写类神器:attrs
https://segmentfault.com/a/1190000019806848
2019-07-18T23:41:06+08:00
2019-07-18T23:41:06+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>使用attrs解放双手</h2>
<p>大家好,这一期我想和大家分享一个OOP编程的高效神器:attrs库</p>
<p>首先我们来介绍下 attrs 这个库,其官方的介绍如下:</p>
<blockquote>attrs 是这样的一个 Python 工具包,它能将你从繁综复杂的实现上解脱出来,享受编写 Python 类的快乐。它的目标就是在不减慢你编程速度的前提下,帮助你来编写简洁而又正确的代码。</blockquote>
<p>因此用了它,定义和实现 Python 类变得更加简洁和高效</p>
<p>首先明确一点,我们现在是装了 attrs 和 cattrs 这两个库,但是实际导入的时候是使用 attr 和 cattr 这两个包,是不带 s 的。</p>
<p>在 attr 这个库里面有两个比较常用的组件叫做 attrs 和 attr,前者是主要用来修饰一个自定义类的,后者是定义类里面的一个字段的。下面是一个小例子</p>
<pre><code class="python">
from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str,default="")
age = attrib(type = int,default=0)
sex = attrib(type = str,default="")
if __name__ == '__main__':
first_person = Person("John",18,"M")
print(first_person)
Out:Person(name='John', age=18, sex='M')
</code></pre>
<h2>主要作用</h2>
<p>可以发现,Person这个类 三个属性都只写了一次,同时还指定了各个字段的类型和默认值,另外也不需要再定义 <strong>init</strong> 方法和 <strong>repr</strong> 方法了,非常简洁</p>
<p>实际上,主要是 attrs 这个修饰符起了作用,然后根据定义的 attrib 属性自动帮我们实现了 <strong>init</strong>、<strong>repr</strong>、<strong>eq</strong>、<strong>ne</strong>、<strong>lt</strong>、<strong>le</strong>、<strong>gt</strong>、<strong>ge</strong>、<strong>hash</strong> 这几个方法</p>
<h2>深入了解</h2>
<p>现在来用实例看一下:</p>
<pre><code class="python">first_person = Person("John",18,"M")
second_person = Person("Nancy",16,"F")
print('Equal:', first_person == second_person) #False
print('Not Equal(ne):', first_person != second_person) #True
print('Less Than(lt):', first_person.age < second_person.age) #False
print('Less or Equal(le):', first_person.age <= second_person.age) #False
print('Greater Than(gt):', first_person.age > second_person.age) #True
print('Greater or Equal(ge):', first_person.age >= second_person.age) #True</code></pre>
<h2>属性定义</h2>
<p>对于 attrib 的定义,可以传入各种参数,不同的参数对于这个类的定义有非常大的影响。</p>
<p>下面来详细了解一下每个属性的具体参数和用法。</p>
<p>首先我们用 attrs 里面的 fields 方法可以查看一下</p>
<pre><code class="python">from attr import attrs, attrib,fields
print(fields(Person))
(Attribute(name='name', default='', validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'str'>, converter=None, kw_only=False), Attribute(name='age', default=0, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False), Attribute(name='sex', default='', validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'str'>, converter=None, kw_only=False))
</code></pre>
<table>
<thead><tr>
<th>参数</th>
<th>解释</th>
</tr></thead>
<tbody>
<tr>
<td>name</td>
<td>属性的名字,是一个字符串类型</td>
</tr>
<tr>
<td>default</td>
<td>属性的默认值,如果没有传入初始化数据,那么就会使用默认值,如果没有默认值定义,那么就是 NOTHING,即没有默认值</td>
</tr>
<tr>
<td>validator</td>
<td>验证器,检查传入的参数是否合法</td>
</tr>
<tr>
<td>init</td>
<td>是否参与初始化,如果为 False,那么这个参数不能当做类的初始化参数,默认是 True。</td>
</tr>
<tr>
<td>type</td>
<td>类型,比如 int、str 等各种类型,默认为 None</td>
</tr>
<tr>
<td>metadata</td>
<td>元数据,只读性的附加数据</td>
</tr>
<tr>
<td>converter</td>
<td>转换器,进行一些值的处理和转换器,增加容错性</td>
</tr>
<tr>
<td>kw_only</td>
<td>是否为强制关键字参数,默认为 False</td>
</tr>
</tbody>
</table>
<h3>初始化</h3>
<p>如果一个类的某些属性不想参与初始化,比如想直接设置一个初始值,一直固定不变,我们可以将属性的 init 参数设置为 False,看一个实例:</p>
<pre><code>from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str)
age = attrib(init=False)
sex = attrib(type = str)
first = Person("John","M") # Person(name='John', age=NOTHING, sex='M')
second = Person("Mike",89,"M")
#TypeError: __init__() takes 3 positional arguments but 4 were given</code></pre>
<p>可以发现,first没有问题,但是second会报错,因为age没有参与初始化,只剩了self,age,sex</p>
<h3>强制关键字</h3>
<p>强制关键字是 Python 里面的一个特性,在传入的时候必须使用关键字的名字来传入</p>
<p>设置了强制关键字参数的属性必须要放在后面,其后面不能再有非强制关键字参数的属性</p>
<p>我们还是拿一样的例子,这回把sex的参数</p>
<pre><code class="python">from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str)
age = attrib(type = str)
sex = attrib(kw_only=True)
first = Person("John",18,sex="M")
#Person(name='John', age=18, sex='M')
</code></pre>
<p>如果初始化first时使用Person("John",18,"M")则会报错</p>
<h3>验证器</h3>
<p>有时候在设置一个属性的时候必须要满足某个条件,比如性别必须要是男或者女,否则就不合法。对于这种情况,我们就需要有条件来控制某些属性不能为非法值。</p>
<pre><code class="python">from attr import attrs, attrib
def is_valid_gender(instance, attribute, value):
if value not in ('M', 'F'):
raise ValueError(f'gender {value} is not valid')
@attrs
class Person:
name = attrib(type = str)
age = attrib(type = str)
sex = attrib(validator=is_valid_gender)</code></pre>
<p>在这里我们定义了一个验证器 Validator 方法,叫做 is_valid_gender,其中 gender 定义的时候传入了一个参数 validator,其值就是我们定义的 Validator 方法:</p>
<ul>
<li>instance:类对象</li>
<li>attribute:属性名</li>
<li>value:属性值</li>
</ul>
<p>下面做了两个实验,一个就是正常传入 "M",另一个写错了,写的是 "X":</p>
<pre><code class="python">
first = Person("John",18,"M")
# Person(name='John', age=18, sex='M')
second = Person("Ann",29,"X")
ValueError: gender X is not valid</code></pre>
<p>second报错了,因为其值不是正常的性别,所以程序直接报错终止<br>注意在 Validator 里面返回 True 或 False 是没用的,错误的值还会被照常复制。所以,一定要在 Validator 里面 raise 某个错误。</p>
<p>另外 attrs 库里面还给我们内置了好多 Validator,比如判断类型,这里如果规定age必须为 int 类型:</p>
<pre><code class="python">age =attrib(validator=validators.instance_of(int))</code></pre>
<p>另外 validator 参数还支持多个 Validator,比如我们要设置既要是数字,又要小于 100,那么可以把几个 Validator 放到一个列表里面并传入:</p>
<pre><code class="python">from attr import attrs, attrib,validators
def is_valid_gender(instance, attribute, value):
if value not in ('M', 'F'):
raise ValueError(f'gender {value} is not valid')
def is_less_than_100(instance, attribute, value):
if value > 100:
raise ValueError(f'age {value} must less than 100')
@attrs
class Person:
name = attrib(type = str)
age = attrib(validator=[validators.instance_of(int), is_less_than_100])
sex = attrib(validator=[validators.instance_of(str),is_valid_gender])
</code></pre>
<h3>转换器</h3>
<p>很多时候我们会不小心传入一些形式不太标准的结果,比如本来是 int 类型的 100,我们传入了字符串类型的 100,那这时候直接抛错应该不好吧,所以我们可以设置一些转换器来增强容错机制,比如将字符串自动转为数字:</p>
<pre><code class="python">from attr import attrs, attrib,validators
def to_int(value):
try:
return int(value)
except:
return None
@attrs
class Person:
name = attrib(type = str)
age = attrib(converter=to_int)
sex = attrib(validator=validators.instance_of(str))
last_person = Person("xiaobai","35","M")
print(last_person)
Out: Person(name='xiaobai', age=35, sex='M')</code></pre>
<h2>总结</h2>
<p>这次我记录了attrs 库的用法,是参考了别人的文章,至此Python OOP 编程的个人学习经历都已经记录下来了</p>
<p>我的其他原创文章已经放到了Github上,如果感兴趣的朋友可以去看看,链接如下:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=iwwTFRQXhr%2F9Ma0dOjS2JA%3D%3D.sVHcpOAMj1JEnFSl8pqqlBrsIKvc7uDDMiwAFDy5sDuMPgXR02ij4SH0cbhVMwZnnRi2eNY%2F6GaAzI7eyFKKRg%3D%3D" rel="nofollow">Python 精品练习题100道</a></li>
<li><a href="https://link.segmentfault.com/?enc=82jgyu4lXrRQ11t%2FiUBRXw%3D%3D.ETbb8kxOs2DzSbun5U0ucf6zFQmZ4LYfRBjddueZH9yRCExk%2FDDb5Ok54K7xM4HOKECt%2BhZo%2Bty3pfFFiLGkjw%3D%3D" rel="nofollow">Python 实用技巧汇总</a></li>
<li><a href="https://link.segmentfault.com/?enc=dd9K2x9yYb3ZdllXbN%2FPEA%3D%3D.96UGqsD8PQorove1NkggOTofbTUFS6A%2B73uUB1vFvXVPlfHUTlNFbjCwmFbqkFUN" rel="nofollow">Python Pandas教程</a></li>
</ul>
Python 面向对象编程OOP (三) 类方法,静态方法
https://segmentfault.com/a/1190000019793521
2019-07-17T20:05:06+08:00
2019-07-17T20:05:06+08:00
alpha94511
https://segmentfault.com/u/alpha94511
0
<h2>类的方法概览</h2>
<p>首先回顾一下Python OOP常见的三种方法:</p>
<ul>
<li>instance method 实例/接口方法</li>
<li>class method 类方法</li>
<li>static method 静态方法</li>
</ul>
<p>标准书写方式如下:</p>
<pre><code class="python">class MyClass:
def method(self):
return 'instance method called', self
@classmethod
def classmethod(cls):
return 'class method called', cls
@staticmethod
def staticmethod():
return 'static method called'</code></pre>
<p>我们最常用的其实就是普通的接口方法,其他两个需要用类似装饰器的写法来标注。</p>
<p>类方法接受一个cls作为参数,它是指向MyClass本身的,并不是MyClass所创建的实例。</p>
<p>静态方法不接受self或者cls作为参数,所以不会修改类本身或者实例</p>
<h2>类方法示例</h2>
<p>看一下如何使用类方法,新建一个Pizza类,主要参数为原料ingredients</p>
<pre><code class="python">class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'Pizza({self.ingredients !r})'</code></pre>
<p>新建一个实例测试一下:</p>
<pre><code class="python">Pizza(['cheese', 'tomatoes'])
Out: Pizza(['cheese', 'tomatoes'])</code></pre>
<p>测试成功。<br>现在问题来了,既然是Pizza类,会有不同口味的Pizza,他们的配方都是固定的,那么如何便捷的生成不同口味的Pizza呢,答案就是classmethod</p>
<pre><code class="python">class Pizza:
def __init__(self, ingredients):
self.ingredients = ingredients
def __repr__(self):
return f'Pizza({self.ingredients!r})'
@classmethod
def margherita(cls):
return cls(['mozzarella', 'tomatoes'])
@classmethod
def prosciutto(cls):
return cls(['mozzarella', 'tomatoes', 'ham'])
</code></pre>
<p>类方法可以根据需求事先预定义生成的实例,减少了代码量,这里我们根据margherita和prosciutto两种口味pizza的原料提前准备好了,cls就是代表类本身,这样如果我们再生成一些实例时,会方便很多:</p>
<pre><code class="python">#生成一个margherita口味的pizza
m = Pizza.margherita()
m
Out:Pizza(['mozzarella', 'tomatoes'])</code></pre>
<pre><code class="python">#生成一个prosciutto口味的pizza
p = Pizza.prosciutto()
p
Out:Pizza(['mozzarella', 'tomatoes', 'ham'])
</code></pre>
<h2>静态方法示例</h2>
<p>那么什么时候用静态方法呢? 比如还用这个例子,我想计算pizza的面积:</p>
<pre><code class="python">import math
class Pizza:
def __init__(self, radius, ingredients):
self.radius = radius
self.ingredients = ingredients
def __repr__(self):
return (f'Pizza({self.radius!r}, '
f'{self.ingredients!r})')
def area(self):
return self.circle_area(self.radius)
@staticmethod
def circle_area(r):
return r ** 2 * math.pi
</code></pre>
<p>这种情况下使用一个静态方法是一个好的选择,通过area这个普通的接口方法,可以调用circle_area来计算面积,封装性更好:</p>
<pre><code class="python">p = Pizza(4, ['mozzarella', 'tomatoes'])
p.area()
Out: 50.26548245743669</code></pre>
<h2>总结</h2>
<p>这次简单总结了类中的三种方法,通过Pizza类的实例方便理解,如果大家有想和我沟通交流的,欢迎留言,或者点击以下链接访问我的网站:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=SnKRsK9hkvnjHpoLVJwNrg%3D%3D.n%2FABCPrVZjvOdzxbei7KvgRsIx4McRrr01CLspgq31rb9Gq3brvvc2ktxlhB9GOi" rel="nofollow">我的Github 静态博客</a></li></ul>
<p>谢谢阅读</p>
Python 面向对象编程OOP (二) slots,类的多态,继承,复写方法
https://segmentfault.com/a/1190000019782656
2019-07-16T23:39:15+08:00
2019-07-16T23:39:15+08:00
alpha94511
https://segmentfault.com/u/alpha94511
0
<h2>__slots__魔法</h2>
<p>大家好,上一期我重点总结了有关类的基本知识,现在简单回顾一下,顺便加上一个创建类时常用的东西:__slots__</p>
<p>首先创建一个名人类:<strong>Celebrity</strong></p>
<pre><code class="python"> class Celebrity:
# 限定 Celebrity对象只能绑定name, age,domain属性,加速
__slots__ = ['name','age',"domain"]
# Class Attribute
species = 'human'
# Initializer / Instance Attributes
def __init__(self, name, age, domain):
self.name = name
self.age = age
self.domain = domain</code></pre>
<p>可以发现用slots绑定了三个属性给Celebrity,slots的作用主要有两个:</p>
<ul>
<li>如果需要限定自定义类型的对象只能绑定某些属性,可以通过在类中定义__slots__变量来进行限定。需要注意的是__slots__的限定只对当前类的对象生效,对子类并不起任何作用。</li>
<li>加速</li>
</ul>
<p>现在可以做个实验,首先我们把slots绑定的domian属性去掉:</p>
<pre><code class="python">class Celebrity:
# Class Attribute
species = 'human'
__slots__ = ['name', 'age']
# Initializer / Instance Attributes
def __init__(self, name, age,domain):
self.name = name
self.age = age
self.domain = domain
female_leader = Celebrity("Miss Dong",65,"electrical appliance")
# Access the instance attributes
print("{} is {}.".format(
female_leader.name, female_leader.age))
Out:AttributeError: 'Celebrity' object has no attribute 'domain'</code></pre>
<p>会发现报错了,即便我们在init方法中有domain属性,但是由于slots中没有,所以Celebrity类下创建的实例都不能有domain</p>
<p>接下来让我们简单回顾一下如何调用类变量:</p>
<pre><code class="python">female_leader = Celebrity("Miss Dong", 65,"electrical appliance")
male_leader = Celebrity("Jack Ma", 55,"internet")
# Access the instance attributes
print("{} is {} and {} is {}.".format(
female_leader.name, female_leader.age, male_leader.name, male_leader.age))
# Is male_leader a human?
if male_leader.species == "human":
print("{0} is a {1}!".format(male_leader.name, male_leader.species))
Out:
Miss Dong is 65 and Jack Ma is 55.
Jack Ma is a human!</code></pre>
<h2>*args</h2>
<p>其实<em>args应该和 </em>kargs一起来说,但是今天先重点看一下它在对象中的应用,我们现在给Celebrity类新建3个实例,并且我们想知道年龄最大的是谁 ? </p>
<p>这种情况下*args很好用:</p>
<pre><code class="python">a = Celebrity("Miss Dong",65,"electrical appliance")
b = Celebrity("Jack Ma", 55,"internet")
c = Celebrity("Lei Jun", 50,"mobile")
def get_oldest(*args):
return max(args)
print("The big brother is {} years old.".format(get_oldest(a.age, b.age, c.age)))
Out:
The big brother is 65 years old.
</code></pre>
<p>当然,其他的应用场景还有很多,不多列举了</p>
<h2>类的继承</h2>
<p>首先,我们在Celebrity类中新增两个方法:</p>
<ul>
<li>description:对生成的大佬简单描述</li>
<li>speak: 大佬发言</li>
</ul>
<p>完成后的结果如下:</p>
<pre><code class="python">class Celebrity:
__slots__ = ['name', 'age',"domain"]
species = 'human'
def __init__(self, name, age, domain):
self.name = name
self.age = age
self.domain = domain
# instance method
def description(self):
return "{} is {} years old, working in the {} industry".format(self.name, self.age,self.domain)
# instance method
def speak(self, sound):
return "{} says {}".format(self.name, sound)</code></pre>
<p>现在新建两个类InternetBoss,MobileBoss,全部继承于Celebrity类:</p>
<pre><code class="python"> # Child class (inherits from Dog() class)
class InternetBoss(Celebrity):
pass
# Child class (inherits from Dog() class)
class MobileBoss(Celebrity):
pass</code></pre>
<p>如果我们什么都不做,会自动继承父类的 description和speak方法,做个实验,新建li作为InternetBoss的实例:</p>
<pre><code class="python">li = InternetBoss("Robbin",50,"advertisement")</code></pre>
<p>调用description和speak方法:</p>
<pre><code class="python">li.description()
li.speak("What's your problem ?")
Out:
Robbin is 50 years old, working in the advertisement industry
Robbin says: What's your problem ?</code></pre>
<p>再尝试一个MobileBoss的对象:</p>
<pre><code class="python">lei = MobileBoss("leijun", 50,"mobile")
lei.speak("Are you ok ?")
Out:
leijun says: Are you ok ?</code></pre>
<p>可以发现都是一样的</p>
<h2>类的多态, 复写父类的方法</h2>
<p>对于类的多态,各种教程说的都太专业了,我的理解仅仅是:</p>
<ul><li>对父类现有方法A,当新增新的子类时,可以根据需要重写A。</li></ul>
<p>在我们现在的例子中,可以复写description方法:</p>
<pre><code class="python"> class InternetBoss(Celebrity):
def description(self):
print("I'm Internet Boss !")
class MobileBoss(Celebrity):
def description(self):
print("I'm Mobile phone Boss !")</code></pre>
<p>这样InternetBoss类和MobileBoss类生成实例后,会调用自己的description方法:</p>
<pre><code class="python">li = InternetBoss("Robbin",50,"advertisement")
lei = MobileBoss("leijun", 50,"mobile")
li.description()
lei.description()
Out:
I'm Internet Boss !
I'm Mobile phone Boss !
</code></pre>
<h2>isinstance() 和 issubclass()</h2>
<p>Python 有两个判断继承的函数:</p>
<ul>
<li>isinstance() 用于检查实例类型</li>
<li>issubclass() 用于检查类继承</li>
</ul>
<p>现在还用我们的例子说明一下,首先,这是我们现有的三个类:</p>
<pre><code class="python">class Celebrity:
__slots__ = ['name', 'age',"domain"]
species = 'human'
def __init__(self, name, age, domain):
self.name = name
self.age = age
self.domain = domain
def description(self):
print( "{} is {} years old, working in the {} industry".format(self.name, self.age,self.domain))
def speak(self, sound):
print("{} says: {}".format(self.name, sound))
class InternetBoss(Celebrity):
def description(self):
print("I'm Internet Boss !")
class MobileBoss(Celebrity):
def description(self):
print("I'm Mobile phone Boss !")</code></pre>
<p>然后我们分别用不同的类创建三个实例:</p>
<pre><code class="python">mingzhu = Celebrity("Miss Dong",65,"electrical appliance")
ma= InternetBoss("Pony", 48,"internet")
lei = MobileBoss("leijun", 50,"mobile")</code></pre>
<p>现在使用issubclass()判断InternetBoss和MobileBoss是否继承自Celebrity:</p>
<pre><code class="python"># True
issubclass(InternetBoss,Celebrity)
# True
issubclass(MobileBoss,Celebrity)</code></pre>
<p>使用isinstance()查看mingzhu到底是谁的实例:</p>
<pre><code class="python"># True
isinstance(mingzhu,Celebrity)
# False
isinstance(mingzhu,InternetBoss)
# False
isinstance(mingzhu,MobileBoss)</code></pre>
<p>同理查看ma到底是哪个类的实例:</p>
<pre><code class="python"># True
isinstance(ma,Celebrity)
# True
isinstance(ma,InternetBoss)
# False
isinstance(ma,MobileBoss)
</code></pre>
<p>因为InternetBoss是Celebrity子类,所以ma同时是Celebrity和InternetBoss的实例。</p>
<p>如果我们混用了issubclass和isinstance,会报错:</p>
<pre><code class="python">issubclass(ma,InternetBoss)
Out:
TypeError: issubclass() arg 1 must be a class</code></pre>
<h2>子类重写构造函数</h2>
<p>刚才提到了,如果子类没有写构造函数init(),会自动继承父类的init,但我们通常需要子类有不同的初始函数,这样我们就需要自己复写一下,这里以InternetBoss为例:</p>
<pre><code class="python">class InternetBoss(Celebrity):
def __init__(self,name, age, domain,hometown):
super().__init__(name, age, domain)
self.hometown = hometown
def description(self):
print("I'm Internet Boss !")
def __repr__(self):
return f"This is {self.name} speaking !"</code></pre>
<p>使用了super()会保留需要的父类初始化参数,再添加自己的就行了,这里的repr我会下次总结,现在再新建实例:</p>
<h2>总结</h2>
<p>这次我记录了slots用法,*args 的一个使用场景,类的继承,复写父类方法,构造函数等基本概念,剩下的慢慢来,我会一点点补充。。。</p>
<p>Ps: 本文的实例名称均为杜撰,请不要对号入座...</p>
<p>我的其他文章已经放到了Github上,如果感兴趣的朋友可以去看看,链接如下:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=6AzA5MxUa77cdNTCKqadDQ%3D%3D.nFkD%2B8NzrYk%2F8CpWgFGr5bElUAn1XkOyj5kdf2nVt5TtZshGrwnKLnwa8YtORtW3w0vFQsSfyUvrxnwvQLmAqQ%3D%3D" rel="nofollow">Python 精品练习题100道</a></li>
<li><a href="https://link.segmentfault.com/?enc=g6%2BuEkN%2FMS2Ti%2FDveUBHqQ%3D%3D.oJ6MsT3MXEuHMAmWnduNq91aA8xvr1Bakp54BeHbV5y6Tm1tMC1TvGwlv8wtkAAZp5jA5ipqoB%2FfcTxrkgWbjg%3D%3D" rel="nofollow">Python 实用技巧汇总</a></li>
<li><a href="https://link.segmentfault.com/?enc=A7JEAptZIVBRIuTB2CP59A%3D%3D.38clxB6iZCbvCwtfQ%2FYmvx0SYWOPkD5NO7D6DGXQW7Di3tntBEIzPxogGw4IuDVK" rel="nofollow">Python Pandas教程</a></li>
</ul>
Python 面向对象编程OOP (一) 类,对象,属性,访问权限
https://segmentfault.com/a/1190000019767855
2019-07-15T17:28:05+08:00
2019-07-15T17:28:05+08:00
alpha94511
https://segmentfault.com/u/alpha94511
3
<h2>Python面向对象编程之旅</h2>
<h3>OOP编程是什么</h3>
<p>大家好,作为小白,最近学习了很多Python OOP编程的知识,因为脑容量有限,特此一一按照学习顺序记录下来,如果哪里有错误,还请大神尽快指出,以免误导他人。。。</p>
<p>首先让我们简单了解一下何为面向对象编程:</p>
<blockquote>把一组数据结构和处理它们的方法组成对象(object),把相同行为的对象归纳为类(class),通过类的封装(encapsulation)隐藏内部细节,通过继承(inheritance)实现类的特化(specialization)和泛化(generalization),通过多态(polymorphism)实现基于对象类型的动态分派。</blockquote>
<p>这样一说貌似有些复杂,简单来看的话可以参考下面的解释:</p>
<p><img src="/img/remote/1460000019767858?w=1348&h=970" alt="" title=""></p>
<h3>常见概念一览</h3>
<table>
<thead><tr>
<th>概念</th>
<th>解释</th>
</tr></thead>
<tbody>
<tr>
<td><strong>类(Class)</strong></td>
<td>用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例</td>
</tr>
<tr>
<td><strong>类变量</strong></td>
<td>类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用</td>
</tr>
<tr>
<td><strong>数据成员</strong></td>
<td>类变量或者实例变量, 用于处理类及其实例对象的相关的数据</td>
</tr>
<tr>
<td><strong>方法重写</strong></td>
<td>如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写</td>
</tr>
<tr>
<td><strong>局部变量</strong></td>
<td>定义在方法中的变量,只作用于当前实例的类</td>
</tr>
<tr>
<td><strong>实例变量</strong></td>
<td>在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的</td>
</tr>
<tr>
<td><strong>继承</strong></td>
<td>即一个派生类(derived class)继承基类(base class)的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如,有这样一个设计:一个Dog类型的对象派生自Animal类,这是模拟"是一个(is-a)"关系(例图,Dog是一个Animal)</td>
</tr>
<tr>
<td><strong>实例化</strong></td>
<td>创建一个类的实例,类的具体对象</td>
</tr>
<tr>
<td><strong>方法</strong></td>
<td>类中定义的函数</td>
</tr>
<tr>
<td><strong>对象</strong></td>
<td>通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法</td>
</tr>
</tbody>
</table>
<h3>定义一个类</h3>
<p>下面让我们简单定义一个汽车类:</p>
<pre><code class="python">class Car:
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year</code></pre>
<p>这里我们创建了一个汽车类Car,它有三个公共属性,分别是color(颜色),model(型号),year(生产年份)</p>
<h3>创建实例对象,访问属性</h3>
<p>现在让我们新建一个对象my_car:</p>
<pre><code class="python">my_car = Car("yellow", "beetle", 1967)</code></pre>
<p>查看一下my_car的属性</p>
<pre><code class="python">print(f" My {my_car.color} car {my_car.model} is made in {my_car.year}")
# My yellow car beetle is made in 1967</code></pre>
<h3>添加新属性</h3>
<p>我们想要给my_car添加一个新属性wheels</p>
<pre><code class="python">my_car.wheels = 5
print(f"Wheels: {my_car.wheels}")
# Wheels: 5</code></pre>
<p>使用dir(my_car)可以让我们确认一下属性是否存在:</p>
<pre><code class="python">dir(my_car)
Out:
['__class__',
'__delattr__',
'__dict__',
'__dir__',
'__doc__',
'__eq__',
'__format__',
'__ge__',
'__getattribute__',
'__gt__',
'__hash__',
'__init__',
'__init_subclass__',
'__le__',
'__lt__',
'__module__',
'__ne__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__str__',
'__subclasshook__',
'__weakref__',
'color',
'model',
'wheels', <====已经添加成功啦
'year']
</code></pre>
<h3>类变量,修改类变量的值</h3>
<p>在Python中,我们在类外声明一个类变量,下面让我们修改一下Car类:</p>
<pre><code class="python">class Car:
wheels = 0
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year</code></pre>
<p>这样的话,我们在调用wheels这个变量时,可以通过实例,或者直接调用Car.wheels:</p>
<pre><code class="python">my_car = Car("yellow", "beetle", 1967)
print(f"My car is {my_car.color}")
print(f"It has {Car.wheels} wheels")
print(f"It has {my_car.wheels} wheels")
Out:
My car is yellow
It has 0 wheels
It has 0 wheels
</code></pre>
<p>这里需要注意一下,如果想要通过my_car.wheels =xxx来修改wheels的值,不会真正修改类变量wheels的值,我们来看一个具体的例子:</p>
<pre><code class="python">my_car = Car("yellow", "Beetle", "1966")
my_other_car = Car("red", "corvette", "1999")
print(f"My car is {my_car.color}")
print(f"It has {my_car.wheels} wheels")
print(f"My other car is {my_other_car.color}")
print(f"It has {my_other_car.wheels} wheels")
Out:
My car is yellow
It has 0 wheels
My other car is red
It has 0 wheels</code></pre>
<p>我们首先创建两个实例my_car 和my_other_car ,默认的wheels=0,下面我们首先直接通过Car这个类来修改类变量的值:</p>
<pre><code class="python"># Change the class variable value
Car.wheels = 4
print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")
Out:
My car has 4 wheels
My other car has 4 wheels
</code></pre>
<p>可以看到这样修改的话,Car类拥有的所有实例中的wheels值会被全部修改,如果我们通过my_other_car 来修改呢?</p>
<pre><code class="python"># Change the instance variable value for my_car
my_car.wheels = 5
print(f"My car has {my_car.wheels} wheels")
print(f"My other car has {my_other_car.wheels} wheels")
Out:
My car has 5 wheels
My other car has 4 wheels
</code></pre>
<p>现在大家可以发现区别了,仅仅是修改了my_car中wheels的值,对类本身不会造成影响</p>
<h3>私有和公有属性</h3>
<p>在Python中的所有属性都是public,可能有c++和java的同学觉得神奇,其实python最初规定了一种特殊的命名方式来区分public还是private,那就是下划线_</p>
<p>我还是拿一样的例子说明:</p>
<pre><code class="python">class Car:
wheels = 0
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year
self._cupholders = 6
my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")
Out:
It was built in 1969</code></pre>
<p>这里Car类中的杯托 _cupholders就是“私有“属性,为什么我这里加上了引号,是因为Python只是名义上规定这种写法,但是在实际访问上没啥卵用,依然可以直接用._cupholders来访问:</p>
<pre><code class="python">my_car.year = 1966
print(f"It was built in {my_car.year}")
print(f"It has {my_car._cupholders} cupholders.")
Out:
It was built in 1966
It has 6 cupholders.
</code></pre>
<p>后来Python决定使用双下划线__来替换单下划线,这样可以最大程度避免“意外访问“,然而还是没有卵用,再来展示一下新方案:</p>
<pre><code class="python">class Car:
wheels = 0
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year
self.__cupholders = 6
</code></pre>
<p>其实某种程度上,这回效果还是很明显的,如果我们还像刚才一样尝试调用my_car.cupholders 会报错:</p>
<pre><code>my_car = Car("yellow", "Beetle", "1969")
print(f"It was built in {my_car.year}")
print(f"It has {my_car.__cupholders} cupholders.")
Out:
It was built in 1969
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-108-1efe56f0c054> in <module>
1 my_car = Car("yellow", "Beetle", "1969")
2 print(f"It was built in {my_car.year}")
----> 3 print(f"It has {my_car.__cupholders} cupholders.")
AttributeError: 'Car' object has no attribute '__cupholders'</code></pre>
<p>这个错误很有意思,为什么会说cupholders这个变量不存在呢 ? 因为当Python看到__ 时,会自动在cupholders前面补上一个下划线_和所属类名,也就是说,这里我们尝试用my_car.__cupholders 来调用时,Python默认的正确写法是<br>my_car._Car__cupholders,现在再试一下:</p>
<pre><code>print(f"It has {my_car._Car__cupholders} cupholders")
Out: It has 6 cupholders</code></pre>
<p>看见没,依然没拦住。。。。<br>不过我个人认为这种规定公有私有变量的方式也是好处多多,这里就仁者见仁,智者见智了~</p>
<h3>访问权限管理</h3>
<p>就像刚刚提到的,Python所有的东西都是公有的,我们可以随意的新增,修改,甚至删除变量:</p>
<pre><code>my_car = Car("yellow", "beetle", 1969)
print(f"My car was built in {my_car.year}")
my_car.year = 2003
print(f"It was built in {my_car.year}")
del my_car.year
print(f"It was built in {my_car.year}")
Out:
My car was built in 1969
It was built in 2003
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-110-46914b0bae82> in <module>
6
7 del my_car.year
----> 8 print(f"It was built in {my_car.year}")
AttributeError: 'Car' object has no attribute 'year'
</code></pre>
<p>那我们如何才能控制属性的访问权限呢?Python给出的答案是装饰器 @property,这个类似于Java中的setter和getter,现在我们试试:</p>
<pre><code class="python">class Car:
def __init__(self, color, model, year):
self.color = color
self.model = model
self.year = year
self._voltage = 12
@property
def voltage(self):
return self._voltage
@voltage.setter
def voltage(self, volts):
print("Warning: this can cause problems!")
self._voltage = volts
@voltage.deleter
def voltage(self):
print("Warning: the radio will stop working!")
del self._voltage</code></pre>
<p>我们新增了voltage(电压)这个属性,并用property来控制外部的访问权限,这里我们定义了三个方法,利用setter方法可以改变voltage的值,利用getter方法来访问,利用deleter方法实现删除,接下来让我们新建实例来看看propert是如何工作的:</p>
<pre><code class="python">my_car = Car("yellow", "beetle", 1969)
print(f"My car uses {my_car.voltage} volts")
my_car.voltage = 6
print(f"My car now uses {my_car.voltage} volts")
del my_car.voltage
Out:
My car uses 12 volts
Warning: this can cause problems!
My car now uses 6 volts
Warning: the radio will stop working!</code></pre>
<p>可以发现,我们这里直接使用.voltage 而不是._voltage,这样就告诉python去使用property装饰的方法,我们可以通过使用@.setter and @.deleter 使属性变为read-only(只读),从而保护voltage不会被随意修改和删除</p>
<h3>总结</h3>
<p>今天主要总结了OOP编程中的类,对象,属性,公有私有属性,访问权限这些基础概念,下一篇文章会进一步深入,如果本文有哪些语言使用不当,希望大家可以指出,让我们一起进步! </p>
<p>我之前的一些文章已经放到了Github上,如果感兴趣的朋友可以去看看,链接如下:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=apHdnqThJiwWcCcMb%2FAnfw%3D%3D.Ef2gNZDm1dR7fxiDuqGBVBQX5Cml9ge0EJsoDinl4Owo3jXtXyU%2FR85RN07AOuAq47L8e%2FXS97g4k8M%2Fje0eWQ%3D%3D" rel="nofollow">Python 精品练习题100道</a></li>
<li><a href="https://link.segmentfault.com/?enc=NrMRAJxBocGkz%2FSCcJZ%2BDg%3D%3D.enjeMzuUgsT4zO7TVr9dxDdyJB5vB5zYj5Mhg0hmv%2BSg%2BkWJ%2FKgq8BF7p5XsPkCEKS2M%2BwnoAQPE0k0qV2mx4w%3D%3D" rel="nofollow">Python 实用技巧汇总</a></li>
<li><a href="https://link.segmentfault.com/?enc=JGFR6SOndAS%2FS0N77ClkCQ%3D%3D.egdip7kB4JP7xvFlYZ%2FK8Ith1jGlK1MF%2BprFx%2Bs5%2FyGooxxpEqDGTWG6AvSYSCYk" rel="nofollow">Python Pandas教程</a></li>
</ul>
Python基础练习100题 ( 91~ 100)
https://segmentfault.com/a/1190000019462645
2019-06-13T03:42:29+08:00
2019-06-13T03:42:29+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>刷题继续</h2>
<p>昨天和大家分享了81-90题,今天继续来刷最后的91-100题</p>
<h4><strong>Question 91:</strong></h4>
<blockquote>
<strong><em>Please write a program which accepts a string from console and print it in reverse order.</em></strong><p>**Example:<br>If the following string is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>rise to vote sir</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>ris etov ot esir</code></pre>
<h5>解法一</h5>
<pre><code>s = input()
s = ''.join(reversed(s))
print(s)</code></pre>
<h5>解法二</h5>
<pre><code>s=input()
s = s[::-1]
print(s)</code></pre>
<h4><strong>Question 92:</strong></h4>
<blockquote>
<strong><em>Please write a program which accepts a string from console and print the characters that have even indexes.</em></strong><p><em>*</em>Example:<br>If the following string is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>H1e2l3l4o5w6o7r8l9d</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>Helloworld</code></pre>
<h5>解法一</h5>
<pre><code>s=input()
print(s[::2])</code></pre>
<h5>解法二</h5>
<pre><code>s = "H1e2l3l4o5w6o7r8l9d"
s = [ v for (i,v) in enumerate(s) if i%2 ==0 ]
print(''.join(s))
</code></pre>
<h4><strong>Question 93:</strong></h4>
<blockquote><strong><em>Please write a program which prints all permutations of [1,2,3]</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import itertools
result = list(itertools.permutations([1,2,3]))
print(result)</code></pre>
<h4><strong>Question 94:</strong></h4>
<blockquote>
<em>*</em>Write a program to solve a classic ancient Chinese puzzle: <br>We count 35 heads and 94 legs among the chickens and rabbits in a farm. How many rabbits and how many chickens do we have?<em>*</em> (鸡兔同笼)</blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def solve(numheads,numlegs):
ns='No solutions!'
for i in range(numheads+1):
j=numheads-i
if 2*i+4*j==numlegs:
return i,j
return ns,ns
numheads=35
numlegs=94
solutions=solve(numheads,numlegs)
print(solutions)</code></pre>
<h4><strong>Question 95:</strong></h4>
<blockquote>
<p><strong><em>Given the participants' score sheet for your University Sports Day, you are required to find the runner-up score. You are given scores. Store them in a list and find the score of the runner-up.</em></strong></p>
<p><strong><em>If the following string is given as input to the program:</em></strong></p>
<pre><code>5
2 3 6 6 5</code></pre>
<p><strong><em>Then, the output of the program should be:</em></strong></p>
<pre><code>5</code></pre>
</blockquote>
<h5>解法一</h5>
<pre><code>n = int(input())
arr = map(int, input().split())
arr = list(set(arr))
arr.sort()
print(arr[-2])</code></pre>
<h5>解法二</h5>
<pre><code>n = int(input())
arr = map(int, input().split())
arr = list(set(arr))
print(sorted(arr)[-2])</code></pre>
<h4><strong>Question 96:</strong></h4>
<blockquote>
<p><em>*</em>You are given a string S and width W.<br>Your task is to wrap the string into a paragraph of width.<em>*</em></p>
<p><strong><em>If the following string is given as input to the program:</em></strong></p>
<pre><code>ABCDEFGHIJKLIMNOQRSTUVWXYZ
4</code></pre>
<p><strong><em>Then, the output of the program should be:</em></strong></p>
<pre><code>ABCD
EFGH
IJKL
IMNO
QRST
UVWX
YZ</code></pre>
</blockquote>
<h5>解法一</h5>
<pre><code>import textwrap
def wrap(string, max_width):
string = textwrap.wrap(string,max_width)
string = "\n".join(string)
return string
if __name__ == '__main__':
string, max_width = input(), int(input())
result = wrap(string, max_width)
print(result)
</code></pre>
<h5>解法二</h5>
<pre><code>import itertools as it
def grouper(lst, n, fillvalue=None):
iters = [iter(lst)] * n
return it.zip_longest(*iters, fillvalue=fillvalue) # 默认就是None
string, max_width = input(), int(input())
result = grouper(string, max_width)
print(list(result))
</code></pre>
<h4><strong>Question 97:</strong></h4>
<blockquote>
<p><strong><em>You are given an integer, N. Your task is to print an alphabet rangoli of size N. (Rangoli is a form of Indian folk art based on creation of patterns.)</em></strong></p>
<p><strong><em>Different sizes of alphabet rangoli are shown below:</em></strong></p>
<pre><code>#size 3
----c----
--c-b-c--
c-b-a-b-c
--c-b-c--
----c----
#size 5
--------e--------
------e-d-e------
----e-d-c-d-e----
--e-d-c-b-c-d-e--
e-d-c-b-a-b-c-d-e
--e-d-c-b-c-d-e--
----e-d-c-d-e----
------e-d-e------
--------e--------</code></pre>
</blockquote>
<h5>解法一</h5>
<pre><code>import string
def print_rangoli(size):
n = size
alph = string.ascii_lowercase
width = 4 * n - 3
ans = []
for i in range(n):
left = '-'.join(alph[n - i - 1:n])
mid = left[-1:0:-1] + left
final = mid.center(width, '-')
ans.append(final)
if len(ans) > 1:
for i in ans[n - 2::-1]:
ans.append(i)
ans = '\n'.join(ans)
print(ans)
if __name__ == '__main__':
n = int(input())
print_rangoli(n)
</code></pre>
<h4><strong>Question 98:</strong></h4>
<blockquote><strong><em>You are given a date. Your task is to find what the day is on that date.</em></strong></blockquote>
<p><strong>Input</strong></p>
<blockquote>
<p><strong><em>A single line of input containing the space separated month, day and year, respectively, in MM DD YYYY format.</em></strong></p>
<pre><code>08 05 2015</code></pre>
</blockquote>
<p><strong>Output</strong></p>
<blockquote>
<p><strong><em>Output the correct day in capital letters.</em></strong></p>
<pre><code>WEDNESDAY</code></pre>
</blockquote>
<h5>解法一</h5>
<pre><code>import calendar
month, day, year = map(int, input().split())
dayId = calendar.weekday(year, month, day)
print(calendar.day_name[dayId].upper())
</code></pre>
<h5>解法二</h5>
<pre><code>import datetime
month, day, year = map(int, input().split())
dayId = datetime.date(year, month, day)
print(dayId.strftime("%A"))</code></pre>
<h4><strong>Question 99:</strong></h4>
<blockquote><strong><em>Given 2 sets of integers, M and N, print their symmetric difference in ascending order. The term symmetric difference indicates those values that exist in either M or N but do not exist in both.</em></strong></blockquote>
<p><strong>Input</strong></p>
<blockquote>
<p><strong><em>The first line of input contains an integer, M.The second line contains M space-separated integers.The third line contains an integer, N.The fourth line contains N space-separated integers.</em></strong></p>
<pre><code>4
2 4 5 9
4
2 4 11 12</code></pre>
</blockquote>
<p><strong>Output</strong></p>
<blockquote>
<p><strong><em>Output the symmetric difference integers in ascending order, one per line.</em></strong></p>
<pre><code>5
9
11
12</code></pre>
</blockquote>
<hr>
<h5>解法一</h5>
<pre><code>if __name__ == '__main__':
n = int(input())
set1 = set(map(int,input().split()))
m = int(input())
set2 = set(map(int, input().split()))
ans = list(set1 ^ set2)
print(sorted(ans))
</code></pre>
<h4><strong>Question 100:</strong></h4>
<blockquote>
<p><strong><em>You are given words. Some words may repeat. For each word, output its number of occurrences. The output order should correspond with the input order of appearance of the word. See the sample input/output for clarification.</em></strong></p>
<p><strong><em>If the following string is given as input to the program:</em></strong></p>
<pre><code>4
bcdef
abcdefg
bcde
bcdef</code></pre>
<p><strong><em>Then, the output of the program should be:</em></strong></p>
<pre><code>3
2 1 1</code></pre>
</blockquote>
<h5>解法一</h5>
<pre><code>n = int(input())
word_list = []
word_dict = {}
for i in range(n):
word = input()
if word not in word_dict:
word_list.append(word)
word_dict[word] = word_dict.get(word, 0) + 1
print(len(word_list))
for word in word_list:
print(word_dict[word], end=' ')
</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=S8DribiEva%2BjLiIqg1ZxFA%3D%3D.uFFwjgrqaQIXhHbcEQWYCM2DQqcQWsaFCk%2BK%2B%2F56ji09eWrPN4sFcooCUdETnUaTfinMBiRI4yThcKdUEi%2BfmsiR%2FJj7HmlbExZu1XMyNC4EitumrOrYO4bgpJq6EokXuP5U0eBfeNx5CLKVQ2p9Ag%3D%3D" rel="nofollow">Python 91-100题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>到今天为止,这套题就已经全部结束了,相信大家如果看了每一道题,还是对技能提升有些许帮助的!</p>
<p>如果你有更好的Python学习资料,想要分享或者交流,欢迎给我留言哈!</p>
<ul><li><a href="https://link.segmentfault.com/?enc=Wj8KwZueA9LBbSXi75hNtA%3D%3D.XCiJTF0RumPJsSQq7cmoBsnSXYQ9xZLl9Our%2Fpw4IrmGrcIkOL3atDAE%2FHSbhdIy" rel="nofollow">进入我的个人主页</a></li></ul>
<p>独乐乐不如众乐乐,大家一起进步,谢谢!</p>
Python基础练习100题 ( 81~ 90)
https://segmentfault.com/a/1190000019437615
2019-06-10T22:27:23+08:00
2019-06-10T22:27:23+08:00
alpha94511
https://segmentfault.com/u/alpha94511
0
<h2>刷题继续</h2>
<p>昨天和大家分享了71-80题,今天继续来刷81~90题</p>
<h4><strong>Question 81:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program to print the list after removing numbers which are divisible by 5 and 7 in [12,24,35,70,88,120,155].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>li = [12,24,35,70,88,120,155]
li = [x for x in li if x % 35!=0]
print(li)</code></pre>
<h4><strong>Question 82:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program to print the list after removing the 0th, 2nd, 4th,6th numbers in [12,24,35,70,88,120,155].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>li = [12,24,35,70,88,120,155]
li = [li[i] for i in range(len(li)) if i%2 != 0]
print(li)
</code></pre>
<h5>解法二</h5>
<pre><code>li = [12,24,35,70,88,120,155]
li = [x for (i,x) in enumerate(li) if i%2!=0]
print(li)</code></pre>
<h4><strong>Question 83:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program to print the list after removing the 2nd - 4th numbers in [12,24,35,70,88,120,155].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>li = [12,24,35,70,88,120,155]
li = [x for (i,x) in enumerate(li) if i<3 or 4<i]
print(li)
</code></pre>
<h4><strong>Question 84:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program generate a 3*5*8 3D array whose each element is 0.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>array = [[ [0 for col in range(8)] for col in range(5)] for row in range(3)]
print(array)</code></pre>
<h4><strong>Question 85:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program to print the list after removing the 0th,4th,5th numbers in [12,24,35,70,88,120,155].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>li = [12,24,35,70,88,120,155]
li = [li[i] for i in range(len(li)) if i not in (0,4,5)]
print(li)
</code></pre>
<h4><strong>Question 86:</strong></h4>
<blockquote><strong><em>By using list comprehension, please write a program to print the list after removing the value 24 in [12,24,35,24,88,120,155].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>li = [12,24,35,24,88,120,155]
li.remove(24)
# this will remove only the first occurrence of 24
print(li)
</code></pre>
<h5>解法二</h5>
<pre><code>li = [12,24,35,24,88,120,155]
li = [x for x in li if x!=24] # Delete all 24
print(li)
</code></pre>
<h4><strong>Question 87:</strong></h4>
<blockquote><strong><em>With two given lists [1,3,6,78,35,55] and [12,24,35,24,88,120,155], write a program to make a list whose elements are intersection of the above given lists.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>set1=set([1,3,6,78,35,55])
set2=set([12,24,35,24,88,120,155])
result = set1.intersection(set2)
print(result)
</code></pre>
<h5>解法二</h5>
<pre><code>set1=set([1,3,6,78,35,55])
set2=set([12,24,35,24,88,120,155])
result = set1 & set2
print(result)</code></pre>
<h4><strong>Question 88:</strong></h4>
<blockquote><strong><em>With a given list [12,24,35,24,88,120,155,88,120,155], write a program to print this list after removing all duplicate values with original order reserved.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>
li=[12,24,35,24,88,120,155,88,120,155]
result = set(li) #> Python 3.6 Set keep the order test
print(result) </code></pre>
<h5>解法二</h5>
<pre><code>def removeDuplicate( li ):
seen = {}
for item in li:
if item not in seen:
seen[item] = True
yield item
li = [12, 24, 35, 24, 88, 120, 155, 88, 120, 155]
ans = list(removeDuplicate(li))
print(ans)</code></pre>
<h5>解法三</h5>
<pre><code>li = [12,24,35,24,88,120,155,88,120,155]
for i in li:
if li.count(i) > 1:
li.remove(i)
print(li)
</code></pre>
<h4><strong>Question 89:</strong></h4>
<blockquote><strong><em>Define a class Person and its two child classes: Male and Female. All classes have a method "getGender" which can print "Male" for Male class and "Female" for Female class.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>
class Person(object):
def getGender( self ):
return "Unknown"
class Male( Person ):
def getGender( self ):
return "Male"
class Female( Person ):
def getGender( self ):
return "Female"
# Test
aMale = Male()
aFemale= Female()
print(aMale.getGender())
print (aFemale.getGender())
</code></pre>
<h4><strong>Question 90:</strong></h4>
<blockquote>
<strong><em>Please write a program which count and print the numbers of each character in a string input by console.</em></strong><p><em>*</em>Example:<br>If the following string is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>abcdefgabc</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>a,2
c,2
b,2
e,1
d,1
g,1
f,1</code></pre>
<h5>解法一</h5>
<pre><code>
s=input()
dic = {word:s.count(word) for word in s}
for k, v in dic.items():
print(f"{k},{v}")
</code></pre>
<h5>解法二</h5>
<pre><code>s = input()
for letter in range(ord('a'),ord('z')+1): # ord() gets the ascii value of a char
letter = chr(letter) # chr() gets the char of an ascii value
cnt = s.count(letter)
if cnt > 0:
print("{},{}".format(letter,cnt))</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=bog0hrZV6%2BD1poCT5Mh10A%3D%3D.%2BGi7eipcNzJ7LjWt%2Fe7Z0kf4PFSPDT9wUfcWCw40RwlGyeUNLAE5iz96XVmIGQaoq3McWOeR07MGSYaDUDqzFKYyMQ3nWAaehJwNmnVze35qQ1CxtAGUVfVQBwFma%2F%2Bt8%2BnyT%2BBPx61%2BzMzDalT9qQ%3D%3D" rel="nofollow">Python 81-90题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 71~ 80)
https://segmentfault.com/a/1190000019425151
2019-06-09T21:36:33+08:00
2019-06-09T21:36:33+08:00
alpha94511
https://segmentfault.com/u/alpha94511
0
<h2>刷题继续</h2>
<p>昨天和大家分享了61-70题,今天继续来刷71~80题</p>
<h4><strong>Question 71:</strong></h4>
<blockquote><strong><em>Please write a program to output a random number, which is divisible by 5 and 7, between 10 and 150 inclusive using random module and list comprehension.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
print (random.choice([i for i in range(10,151) if i%5==0 and i%7==0]))</code></pre>
<h5>解法二</h5>
<pre><code>import random
resp = [i for i in range(10,151) if i % 35 == 0 ]
print(random.choice(resp))</code></pre>
<h4><strong>Question 72:</strong></h4>
<blockquote><strong><em>Please write a program to generate a list with 5 random numbers between 100 and 200 inclusive.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
resp = random.sample(range(100,201),5)
print(resp)</code></pre>
<h4><strong>Question 73:</strong></h4>
<blockquote><strong><em>Please write a program to randomly generate a list with 5 even numbers between 100 and 200 inclusive.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
numbers = random.sample(range(100,201,2),5)
print(numbers)</code></pre>
<h5>解法二</h5>
<pre><code class="python">import random
print (random.sample([i for i in range(100,201) if i%2==0], 5))
</code></pre>
<h4><strong>Question 74:</strong></h4>
<blockquote><strong><em>Please write a program to randomly generate a list with 5 numbers, which are divisible by 5 and 7 , between 1 and 1000 inclusive.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
lst = [i for i in range(1,1001) if i%35 == 0]
resp = random.sample(lst,5)
print(resp)</code></pre>
<h4><strong>Question 75:</strong></h4>
<blockquote><strong><em>Please write a program to randomly print a integer number between 7 and 15 inclusive.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
number= random.choice([x for x in range(7,16)])
print(number)</code></pre>
<h5>解法二</h5>
<pre><code>import random
print(random.randrange(7,16))</code></pre>
<h4><strong>Question 76:</strong></h4>
<blockquote><strong><em>Please write a program to compress and decompress the string "hello world!hello world!hello world!hello world!".</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import zlib
s = 'hello world!hello world!hello world!hello world!'.encode()
t = zlib.compress(s)
print(t)
print(zlib.decompress(t))</code></pre>
<h4><strong>Question 77:</strong></h4>
<blockquote><strong><em>Please write a program to print the running time of execution of "1+1" for 100 times.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>from timeit import Timer
t = Timer("for i in range(100):1+1")
t.timeit()</code></pre>
<h5>解法二</h5>
<pre><code>import time
before = time.time()
for i in range(100):
x = 1 + 1
after = time.time()
execution_time = after - before
print(execution_time)</code></pre>
<h4><strong>Question 78:</strong></h4>
<blockquote><strong><em>Please write a program to shuffle and print the list [3,6,7,8].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
lst = [3,6,7,8]
random.shuffle(lst)
print(lst) </code></pre>
<h5>解法二</h5>
<pre><code>import random
lst = [3,6,7,8]
seed = 7
random.Random(seed).shuffle(lst)
print(lst)
</code></pre>
<h4><strong>Question 79:</strong></h4>
<blockquote><strong><em>Please write a program to generate all sentences where subject is in ["I", "You"] and verb is in ["Play", "Love"] and the object is in ["Hockey","Football"].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>subjects=["I", "You"]
verbs=["Play", "Love"]
objects=["Hockey","Football"]
res = [[i, j, k] for i in subjects
for j in verbs
for k in objects]
for x in res:
print(" ".join(x))
Out:
I Play Hockey
I Play Football
I Love Hockey
I Love Football
You Play Hockey
You Play Football
You Love Hockey
You Love Football</code></pre>
<h5>解法二</h5>
<pre><code>
subjects=["I", "You"]
verbs=["Play", "Love"]
objects=["Hockey","Football"]
for sub in subjects:
for verb in verbs:
for obj in objects:
print("{} {} {}".format(sub,verb,obj))
Out:
I Play Hockey
I Play Football
I Love Hockey
I Love Football
You Play Hockey
You Play Football
You Love Hockey
You Love Football
</code></pre>
<h5>解法三</h5>
<pre><code>
import itertools
subjects=["I", "You"]
verbs=["Play", "Love"]
objects=["Hockey","Football"]
all_list = [subjects,verbs,objects]
res = list(itertools.product(*all_list))
for x in res:
print(" ".join(x))
Out:
I Play Hockey
I Play Football
I Love Hockey
I Love Football
You Play Hockey
You Play Football
You Love Hockey
You Love Football</code></pre>
<h4><strong>Question 80:</strong></h4>
<blockquote><strong><em>Please write a program to print the list after removing even numbers in [5,6,77,45,22,12,24].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def isEven(n):
return n%2!=0
li = [5,6,77,45,22,12,24]
lst = list(filter(isEven,li))
print(lst)</code></pre>
<h5>解法二</h5>
<pre><code>li = [5,6,77,45,22,12,24] lst = list(filter(lambda n:n%2!=0,li))
print(lst)</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=A4815LqVqFaCv8wIXvqN1A%3D%3D.IxGaImvf6v%2FnHlPWT08do0UtZmhBBliPC%2F%2FJgUnE6ZyHH9gHPXqOWXfvg2EFa0gfBa10ROVTZ84N5JT9xoAjK8P1Paocjor0LQDc6SiRi2wJNsiQmpbMm%2Foy%2F3nbsmJyY5ZKWGWx4WuyYECLWmK%2FBA%3D%3D" rel="nofollow">Python 71 - 80题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 61~ 70)
https://segmentfault.com/a/1190000019421260
2019-06-09T01:21:07+08:00
2019-06-09T01:21:07+08:00
alpha94511
https://segmentfault.com/u/alpha94511
2
<h2>刷题继续</h2>
<p>昨天和大家分享了51-60题,今天继续来刷61~70题</p>
<h4><strong>Question 61:</strong></h4>
<blockquote><strong><em>The Fibonacci Sequence is computed based on the following formula:</em></strong></blockquote>
<pre><code>f(n)=0 if n=0
f(n)=1 if n=1
f(n)=f(n-1)+f(n-2) if n>1</code></pre>
<blockquote>
<strong><em>Please write a program to compute the value of f(n) with a given n input by console.</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>7</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>13</code></pre>
<h5>解法一</h5>
<pre><code>def f(n):
if n < 2:
return n
return f(n-1) + f(n-2)
n = int(input())
print(f(n))</code></pre>
<h4><strong>Question 62:</strong></h4>
<blockquote><strong><em>The Fibonacci Sequence is computed based on the following formula:</em></strong></blockquote>
<pre><code>f(n)=0 if n=0
f(n)=1 if n=1
f(n)=f(n-1)+f(n-2) if n>1</code></pre>
<blockquote>
<strong><em>Please write a program to compute the value of f(n) with a given n input by console.</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>7</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>0,1,1,2,3,5,8,13</code></pre>
<h5>解法一</h5>
<pre><code>def f(n):
if n < 2:
fibo[n] = n
return fibo[n]
fibo[n] = f(n-1) + f(n-2)
return fibo[n]
n = int(input())
fibo = [0]*(n+1) # initialize a list of size (n+1)
f(n)
fibo = [str(i) for i in fibo]
ans = ",".join(fibo)
print(ans)
</code></pre>
<h4><strong>Question 63:</strong></h4>
<blockquote>
<strong><em>Please write a program using generator to print the even numbers between 0 and n in comma separated form while n is input by console.</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>10</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>0,2,4,6,8,10</code></pre>
<blockquote><strong><em>In case of input data being supplied to the question, it should be assumed to be a console input.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def get_evennumbers(x):
for x in range(0,x+1):
if x%2==0:
yield x
input_number=int(input())
values = []
for i in get_evennumbers(input_number):
values.append(str(i))
print(",".join(values))
</code></pre>
<h4><strong>Question 64:</strong></h4>
<blockquote>
<strong><em>Please write a program using generator to print the numbers which can be divisible by 5 and 7 between 0 and n in comma separated form while n is input by console.</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>100</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>0,35,70</code></pre>
<h5>解法一</h5>
<pre><code>def devision_seven_five(x):
for x in range(0,x+1):
if x%35==0:
yield x
input_number=int(input())
values = []
resp = [str(i) for i in devision_seven_five(input_number)]
print(",".join(resp))</code></pre>
<h4><strong>Question 65:</strong></h4>
<blockquote><strong><em>Please write assert statements to verify that every number in the list [2,4,6,8] is even.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>data = [2,4,5,6]
for i in data:
assert i%2 == 0, "{} is not an even number".format(i)</code></pre>
<h4><strong>Question 66:</strong></h4>
<blockquote>
<strong><em>Please write a program which accepts basic mathematic expression from console and print the evaluation result.</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>35 + 3</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>38</code></pre>
<h5>解法一</h5>
<pre><code>expression = input()
ans = eval(expression)
print(ans)
</code></pre>
<h4><strong>Question 67:</strong></h4>
<blockquote><strong><em>Please write a binary search function which searches an item in a sorted list. The function should return the index of element to be searched in the list.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>from bisect import bisect_right
def BinarySearch(a, x):
i = bisect_right(a, x)
if i != len(a)+1 and a[i-1] == x:
return (i-1)
else:
return -1
lst = [1,2,4,5,6,7,8]
x = int(input())
res = BinarySearch(lst, x)
if res == -1:
print(x, "is absent")
else:
print("Last occurrence of", x, "is present at", res)
</code></pre>
<h4><strong>Question 68:</strong></h4>
<blockquote><strong><em>Please generate a random float where the value is between 10 and 100 using Python module.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
rand_num = random.uniform(10,100)
print(rand_num)
</code></pre>
<h5>解法二</h5>
<pre><code>import random
print(random.random()*100)</code></pre>
<h4><strong>Question 69:</strong></h4>
<blockquote><strong><em>Please generate a random float where the value is between 5 and 95 using Python module.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>
import random
print(random.random()*100-5)
</code></pre>
<h5>解法二</h5>
<pre><code>import random rand_num = random.uniform(5,95)
print(rand_num) </code></pre>
<h4><strong>Question 70:</strong></h4>
<blockquote><strong><em>Please write a program to output a random even number between 0 and 10 inclusive using random module and list comprehension.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import random
resp = [i for i in range(2,11,2)]
print(random.choice(resp))
</code></pre>
<h5>解法二</h5>
<pre><code>import random
even_numbers = [x for x in range(0,11) if x%2==0]
print(random.choice(even_numbers))</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=2Z3J1UVdw7Yy7DsUkUta8A%3D%3D.E%2Bxb8kLJDmHbwbXggkgYQrJhlM0IZykYfj%2BeFtLens%2BQGtPCi99eRtXT4jLbwq1AZWhcTpxembwE46aHX8lBvr6Us2XPyeoBMz2IUGOdunAxEEXLCFs3k%2F18qnZrPS7IZY3qdrNTtn%2BNdQ5IhRuMug%3D%3D" rel="nofollow">Python 61-70题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 51~ 60)
https://segmentfault.com/a/1190000019415543
2019-06-07T16:52:22+08:00
2019-06-07T16:52:22+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>刷题继续</h2>
<p>昨天和大家分享了41-50题,今天继续来刷51~60题</p>
<h4><strong>Question 51:</strong></h4>
<blockquote><strong><em>Write a function to compute 5/0 and use try/except to catch the exceptions.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def divide():
return 5/0
try:
divide()
except ZeroDivisionError as ze:
print("Why you are dividing a number by ZERO!!")
except:
print("Any other exception")</code></pre>
<h4><strong>Question 52:</strong></h4>
<blockquote><strong><em>Define a custom exception class which takes a string message as attribute.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class CustomException(Exception):
"""Exception raised for custom purpose
Attributes:message -- explanation of the error
"""
def __init__(self, message):
self.message = message
num = int(input())
try:
if num < 10:
raise CustomException("Input is less than 10")
elif num > 10:
raise CustomException("Input is grater than 10")
except CustomException as ce:
print("The error raised: " + ce.message)
</code></pre>
<h4><strong>Question 53:</strong></h4>
<blockquote>
<strong><em>Assuming that we have some email addresses in the "username@companyname.com" format, please write program to print the user name of a given email address. Both user names and company names are composed of letters only.</em></strong><p>Example:<br>If the following email address is given as input to the <br>program:</p>
</blockquote>
<pre><code>john@google.com</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>john</code></pre>
<blockquote><strong><em>In case of input data being supplied to the question, it should be assumed to be a console input.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def getname(s):
try:
return s.split("@")[0]
except:
print("This is not a email-adress")
getname("john@google.com")
</code></pre>
<h5>解法二</h5>
<pre><code class="python">import re
email = "john@google.com"
pattern = "(\w+)@\w+.com"
ans = re.findall(pattern,email)
print(ans)
</code></pre>
<h4><strong>Question 54:</strong></h4>
<blockquote>
<strong><em>Assuming that we have some email addresses in the "username@companyname.com" format, please write program to print the company name of a given email address. Both user names and company names are composed of letters only.</em></strong><p>Example:<br>If the following email address is given as input to the program:</p>
</blockquote>
<pre><code>john@google.com</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>google</code></pre>
<h5>解法一</h5>
<pre><code>import re
email = "john@google.com"
pattern = "\w+@(\w+).com"
ans = re.findall(pattern,email)
print(ans)</code></pre>
<h5>解法二</h5>
<pre><code>def getname(s):
try:
return s.replace("@",".").split(".")[1]
except:
print("Something Wrong")
getname("john@google.com")
</code></pre>
<h4><strong>Question 55:</strong></h4>
<blockquote>
<strong><em>Write a program which accepts a sequence of words separated by whitespace as input to print the words composed of digits only.</em></strong><p>Example:<br>If the following words is given as input to the program:</p>
</blockquote>
<pre><code>2 cats and 3 dogs.</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>['2', '3']</code></pre>
<blockquote><strong><em>In case of input data being supplied to the question, it should be assumed to be a console input.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>import re
s = input()
print(re.findall("\d+",s))</code></pre>
<h5>解法二</h5>
<pre><code>import re
s= input()
pattern = "\d+"
ans = re.findall(pattern,s)
print(ans)
</code></pre>
<h5>解法三</h5>
<pre><code>[int(s) for s in input().split() if s.isdigit()]</code></pre>
<h4><strong>Question 56:</strong></h4>
<blockquote><strong><em>Print a unicode string "hello world".</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>unicodeString = u"hello world!"
print(unicodeString)</code></pre>
<h4><strong>Question 57:</strong></h4>
<blockquote><strong><em>Write a program to read an ASCII string and to convert it to a unicode string encoded by utf-8.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>s = input()
u = s.encode('utf-8')
print(u)
</code></pre>
<h4><strong>Question 58:</strong></h4>
<blockquote><strong><em>Write a special comment to indicate a Python source code file is in unicode.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code># -*- coding: utf-8 -*-</code></pre>
<h4><strong>Question 59:</strong></h4>
<blockquote>
<strong><em>Write a program to compute 1/2+2/3+3/4+...+n/n+1 with a given n input by console (n>0).</em></strong><p><em>*</em>Example:<br>If the following n is given as input to the program:<em>*</em></p>
</blockquote>
<pre><code>5</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>3.55</code></pre>
<h5>解法一</h5>
<pre><code>n = int(input())
sum = 0
for i in range(1, n+1):
sum+= i/(i+1)
print(round(sum, 2)) # rounded to 2 decimal point
</code></pre>
<h4><strong>Question 60:</strong></h4>
<blockquote><strong><em>Write a program to compute:</em></strong></blockquote>
<pre><code>f(n)=f(n-1)+100 when n>0
and f(0)=1</code></pre>
<blockquote>
<strong><em>with a given n input by console (n>0).</em></strong><p>Example:<br>If the following n is given as input to the program:</p>
</blockquote>
<pre><code>5</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>501</code></pre>
<h5>解法一</h5>
<pre><code>def f(n):
if n == 0:
return 1
else:
return f(n-1) + 100
n = int(input())
print(f(n))
</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=mArKyLPMA70MG6CF7VeKow%3D%3D.qBSO3%2FN4lhWUEABRTXeR%2B9dj5AU2lULg%2FaIMDNrVA7vNxKpAhOtKEMK7SuQM2yBHzd7OWsUU5BRcz7Kjs9yRa1qjjPyjpsZIWqEhIvfUyZzQttKzVT9eV8CJ5v53%2BTSArVP3ToCwIhDYBlMn75ZJ8g%3D%3D" rel="nofollow">Python 51-60题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 41~ 50)
https://segmentfault.com/a/1190000019412360
2019-06-06T20:28:09+08:00
2019-06-06T20:28:09+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>刷题继续</h2>
<p>大家好,我又回来了,昨天和大家分享了31-40题,今天继续来看41~50题</p>
<h4><strong>Question 41:</strong></h4>
<blockquote><strong><em>Write a program which can map() to make a list whose elements are square of elements in [1,2,3,4,5,6,7,8,9,10].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>
lst=[i for i in range(1,11)]
lst_square = list(map(lambda x:x*x,lst))
print(lst_square)</code></pre>
<h5>解法二</h5>
<pre><code>li = [1,2,3,4,5,6,7,8,9,10]
squaredNumbers = map(lambda x: x**2, li)
print(list(squaredNumbers))</code></pre>
<h4><strong>Question 42:</strong></h4>
<blockquote><strong><em>Write a program which can map() and filter() to make a list whose elements are square of even number in [1,2,3,4,5,6,7,8,9,10].</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>lst=[i for i in range(1,11)]
even_numbers = list(map(lambda x: x**2, filter(lambda x: x%2==0, lst)))
print(even_numbers)
</code></pre>
<h5>解法二</h5>
<pre><code>def even(x):
return x%2==0
def squer(x):
return x*x
li = [1,2,3,4,5,6,7,8,9,10]
li = map(squer,filter(even,li))
print(list(li))</code></pre>
<h4><strong>Question 43:</strong></h4>
<blockquote><strong><em>Write a program which can filter() to make a list whose elements are even number between 1 and 20 (both included).</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>even_numbers = list(filter(lambda x: x%2==0, range(1,21)))
print(even_numbers)</code></pre>
<h5>解法二</h5>
<pre><code class="python">def even(x):
return x%2==0
evenNumbers = filter(even, range(1,21))
print(list(evenNumbers))</code></pre>
<h4><strong>Question 44:</strong></h4>
<blockquote><strong><em>Write a program which can map() to make a list whose elements are square of numbers between 1 and 20 (both included).</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def sqr(x):
return x*x
squaredNumbers = list(map(sqr, range(1,21)))
print (squaredNumbers)</code></pre>
<h5>解法二</h5>
<pre><code>squaredNumbers = list(map(lambda x: x**2, range(1,21)))
print(squaredNumbers)</code></pre>
<h4><strong>Question 45:</strong></h4>
<blockquote><strong><em>Define a class named American which has a static method called printNationality.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class American():
@staticmethod
def printNationality():
print("I am American")
american = American()
american.printNationality() # this will not run if @staticmethod does not decorates the function.Because the class has no instance.
American.printNationality() # this will run even though the @staticmethod does not decorate printNationality() </code></pre>
<h4><strong>Question 46:</strong></h4>
<blockquote><strong><em>Define a class named American and its subclass NewYorker.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class American():
pass
class NewYorker(American):
pass
american = American()
newyorker = NewYorker()
print(american)
print(newyorker)</code></pre>
<h4><strong>Question 47:</strong></h4>
<blockquote><strong><em>Define a class named Circle which can be constructed by a radius. The Circle class has a method which can compute the area.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class Circle:
def __init__(self,radius):
self.radius = radius
def area(self):
return (self.radius**2*3.14)
# Test
circle = Circle(5)
print(circle.area())</code></pre>
<h4><strong>Question 48:</strong></h4>
<blockquote><strong><em>Define a class named Rectangle which can be constructed by a length and width. The Rectangle class has a method which can compute the area.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class Rectangle():
def __init__(self,l,w):
self.length = l
self.width = w
def area(self):
return self.length*self.width
rect = Rectangle(2,4)
print(rect.area())</code></pre>
<h4><strong>Question 49:</strong></h4>
<blockquote><strong><em>Define a class named Shape and its subclass Square. The Square class has an init function which takes a length as argument. Both classes have a area function which can print the area of the shape where Shape's area is 0 by default.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>class Shape():
def __init__(self):
pass
def area(self):
return 0
class Square(Shape):
def __init__(self,length = 0):
Shape.__init__(self)
self.length = length
def area(self):
return self.length*self.length
Asqr = Square(5)
print(Asqr.area()) # prints 25
print(Square().area()) # prints à</code></pre>
<h4><strong>Question 50:</strong></h4>
<blockquote><strong><em>Please raise a RuntimeError exception.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>
raise RuntimeError('something wrong')
</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=pueNw5b4VRMUEzmRYS2DyA%3D%3D.ukdyWCQMyFtpikonV4taMdNIiTo%2B00AFhMNL2SVL30eLGydtXw0kVgiEZe4dJj2x8xS3FwgvObD588m7DOuidfjeMSF5wZHVWBZhGUioo23npFFbdv4ZwvKKkk7MY9seUBRB7CivR7SVVfjPAdK5Qg%3D%3D" rel="nofollow">Python 41-50题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 31~ 40)
https://segmentfault.com/a/1190000019401025
2019-06-05T20:33:12+08:00
2019-06-05T20:33:12+08:00
alpha94511
https://segmentfault.com/u/alpha94511
2
<h2>刷题继续</h2>
<p>昨天和大家分享了21-30题,今天继续来刷31~40题</p>
<h4><strong>Question 31:</strong></h4>
<blockquote><strong><em>Define a function which can print a dictionary where the keys are numbers between 1 and 20 (both included) and the values are square of keys.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printDict():
d=dict()
for i in range(1,21):
d[i]=i**2
print(d)
printDict()</code></pre>
<h5>解法二</h5>
<pre><code>def printDict():
dict={i:i**2 for i in range(1,21)}
print(dict)
printDict()</code></pre>
<h4><strong>Question 32:</strong></h4>
<blockquote><strong><em>Define a function which can generate a dictionary where the keys are numbers between 1 and 20 (both included) and the values are square of keys. The function should just print the keys only.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printDict():
dict = {i: i**2 for i in range(1, 21)}
print(dict.keys())
printDict()
</code></pre>
<h5>解法二</h5>
<pre><code>def printDict():
d=dict()
for i in range(1,21):
d[i]=i**2
for k in d.keys():
print(k)
printDict()</code></pre>
<h4><strong>Question 33:</strong></h4>
<blockquote><strong><em>Define a function which can generate and print a list where the values are square of numbers between 1 and 20 (both included).</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printList():
lst = [i ** 2 for i in range(1, 21)]
print(lst)
printList()</code></pre>
<h4><strong>Question 34:</strong></h4>
<blockquote><strong><em>Define a function which can generate a list where the values are square of numbers between 1 and 20 (both included). Then the function needs to print the first 5 elements in the list.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printList():
lst = [i ** 2 for i in range(1, 21)]
print(lst[:5])
printList()</code></pre>
<h4><strong>Question 35:</strong></h4>
<blockquote><strong><em>Define a function which can generate a list where the values are square of numbers between 1 and 20 (both included). Then the function needs to print the last 5 elements in the list.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printList():
lst = [i ** 2 for i in range(1, 21)]
print(lst[-5:])
printList()</code></pre>
<h4><strong>Question 36:</strong></h4>
<blockquote><strong><em>Define a function which can generate a list where the values are square of numbers between 1 and 20 (both included). Then the function needs to print all values except the first 5 elements in the list.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printList():
lst = [i ** 2 for i in range(1, 21)]
print(lst[5:])
printList()</code></pre>
<h4><strong>Question 37:</strong></h4>
<blockquote><strong><em>Define a function which can generate and print a tuple where the value are square of numbers between 1 and 20 (both included).</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>def printTuple():
lst = [i ** 2 for i in range(1, 21)]
print(tuple(lst))
printTuple()</code></pre>
<h4><strong>Question 38:</strong></h4>
<blockquote><strong><em>With a given tuple (1,2,3,4,5,6,7,8,9,10), write a program to print the first half values in one line and the last half values in one line.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>tpl = (1,2,3,4,5,6,7,8,9,10)
for i in range(0,5):
print(tpl[i],end = ' ')
print()
for i in range(5,10):
print(tpl[i],end = ' ')
</code></pre>
<h5>解法二</h5>
<pre><code>tp = tuple(i for i in range(1,11))
lst1,lst2 = list(tp[:5]),list(tp[5:])
print(lst1)
print(lst2)</code></pre>
<h4><strong>Question 39:</strong></h4>
<blockquote><strong><em>Write a program to generate and print another tuple whose values are even numbers in the given tuple (1,2,3,4,5,6,7,8,9,10).</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>tpl = (1,2,3,4,5,6,7,8,9,10)
tpl_even = tuple(i for i in tpl if i%2 == 0)
print(tpl_even)</code></pre>
<h5>解法二</h5>
<pre><code>tpl = (1,2,3,4,5,6,7,8,9,10)
tpl_even= tuple(filter(lambda x : x%2==0,tpl))
print(tpl_even)</code></pre>
<h4><strong>Question 40:</strong></h4>
<blockquote><strong><em>Write a program which accepts a string as input to print "Yes" if the string is "yes" or "YES" or "Yes", otherwise print "No".</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code>s = input()
if s.lower() == 'yes':
print('Yes')
else:
print("No")</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=EgasSJgXpwA86p2sKXqw3w%3D%3D.2B5ROFiAN5X%2FRJXYFK5Q%2F4KH5YvVVlcESTvbIX0ksHv9ubdPdCUJJzv8oGyi1km4lOPQQYV9A9rxpCfOyZcXMrYD84KyGVx12eYaUhsAzTKzMaEOGSlPElftQtgeO9tDpgSPMx1HzKESB5EW%2BKZvGg%3D%3D" rel="nofollow">Python 31-40题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 21~ 30)
https://segmentfault.com/a/1190000019387391
2019-06-04T17:57:57+08:00
2019-06-04T17:57:57+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>刷题继续</h2>
<p>昨天和大家分享了前10道题,今天继续来刷21~30</p>
<h4><strong>Question 21:</strong></h4>
<blockquote><strong><em>A robot moves in a plane starting from the original point (0,0). The robot can move toward UP, DOWN, LEFT and RIGHT with a given steps. The trace of robot movement is shown as the following:</em></strong></blockquote>
<pre><code>UP 5
DOWN 3
LEFT 3
RIGHT 2</code></pre>
<blockquote>
<strong><em>The numbers after the direction are steps. Please write a program to compute the distance from current position after a sequence of movement and original point. If the distance is a float, then just print the nearest integer.</em></strong><br><strong><em>Example:</em></strong><br><strong><em>If the following tuples are given as input to the program:</em></strong>
</blockquote>
<pre><code>UP 5
DOWN 3
LEFT 3
RIGHT 2</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>2</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">import math
x,y = 0,0
while True:
s = input().split()
if not s:
break
if s[0]=='UP': # s[0] indicates command
x-=int(s[1]) # s[1] indicates unit of move
if s[0]=='DOWN':
x+=int(s[1])
if s[0]=='LEFT':
y-=int(s[1])
if s[0]=='RIGHT':
y+=int(s[1])
# N**P means N^P
dist = round(math.sqrt(x**2 + y**2)) # euclidean distance = square root of (x^2+y^2) and rounding it to nearest integer
print(dist)</code></pre>
<h4><strong>Question 22:</strong></h4>
<blockquote>
<strong><em>Write a program to compute the frequency of the words from the input. The output should output after sorting the key alphanumerically.</em></strong><p><strong><em>Suppose the following input is supplied to the program:</em></strong></p>
</blockquote>
<pre><code>New to Python or choosing between Python 2 and Python 3? Read Python 2 or Python 3.</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>2:2
3.:1
3?:1
New:1
Python:5
Read:1
and:1
between:1
choosing:1
or:2
to:1</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">ss = input().split()
word = sorted(set(ss)) # split words are stored and sorted as a set
for i in word:
print("{0}:{1}".format(i,ss.count(i)))</code></pre>
<h5>解法二</h5>
<pre><code class="python">ss = input().split()
dict = {}
for i in ss:
i = dict.setdefault(i,ss.count(i))
dict = sorted(dict.items())
for i in dict:
print("%s:%d"%(i[0],i[1]))</code></pre>
<h5>解法三</h5>
<pre><code class="python">from collections import Counter
ss = input().split()
ss = Counter(ss) # returns key & frequency as a dictionary
ss = sorted(ss.items()) # returns as a tuple list
for i in ss:
print("%s:%d"%(i[0],i[1]))</code></pre>
<h4><strong>Question 23:</strong></h4>
<blockquote><strong><em>Write a method which can calculate square value of number</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">def square(num):
return num ** 2
print(square(2))
print(square(3))</code></pre>
<h5>解法二</h5>
<pre><code class="python">n=int(input())
print(n**2)</code></pre>
<h4><strong>Question 24:</strong></h4>
<blockquote>
<strong><em>Python has many built-in functions, and if you do not know how to use it, you can read document online or find some books. But Python has a built-in document function for every built-in functions.</em></strong><p><strong><em>Please write a program to print some Python built-in functions documents, such as abs(), int(), raw_input()</em></strong></p>
<p><strong><em>And add document for your own function</em></strong></p>
</blockquote>
<h5>解法一</h5>
<pre><code class="python">print (abs.__doc__)
print (int.__doc__)
def square(num):
'''
Return the square value of the input number.
The input number must be integer.
'''
return num ** 2
print (square(2))
print (square.__doc__)</code></pre>
<h4><strong>Question 25:</strong></h4>
<blockquote><strong><em>Define a class, which have a class parameter and have a same instance parameter.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">class Car:
name = "Car"
def __init__(self,name = None):
self.name = name
honda=Car("Honda")
print("%s name is %s"%(Car.name,honda.name))
toyota=Car()
toyota.name="Toyota"
print("%s name is %s"%(Car.name,toyota.name))</code></pre>
<h5>解法二</h5>
<pre><code class="python">class Person:
# Define the class parameter "name"
name = "Person"
def __init__(self, name = None):
# self.name is the instance parameter
self.name = name
jeffrey = Person("Jeffrey")
print ("{0} name is {1}".format(Person.name, jeffrey.name))
nico = Person()
nico.name = "Nico"
print (f"{Person.name} name is {nico.name}")</code></pre>
<h4><strong>Question 26:</strong></h4>
<blockquote><strong><em>Define a function which can compute the sum of two numbers.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">sum = lambda n1,n2 : n1 + n2 # here lambda is use to define little function as sum
print(sum(1,2))</code></pre>
<h5>解法二</h5>
<pre><code class="python">def SumFunction(number1, number2):
return number1 + number2
print SumFunction(1,2)</code></pre>
<h4><strong>Question 27:</strong></h4>
<blockquote><strong><em>Define a function that can convert a integer into a string and print it in console.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">def printValue(n):
print (str(n))
printValue(3)</code></pre>
<h5>解法二</h5>
<pre><code class="python">conv = lambda x : str(x)
n = conv(10)
print(n)
print(type(n)) </code></pre>
<h4><strong>Question 28:</strong></h4>
<blockquote><strong><em>Define a function that can receive two integer numbers in string form and compute their sum and then print it in console.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">def printValue(s1,s2):
print int(s1) + int(s2)
printValue("3","4") #7</code></pre>
<h5>解法二</h5>
<pre><code class="python">sum = lambda s1,s2 : int(s1) + int(s2)
print(sum("10","45")) # 55</code></pre>
<h4><strong>Question 29:</strong></h4>
<blockquote><strong><em>Define a function that can accept two strings as input and concatenate them and then print it in console.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">def printValue(s1,s2):
print s1 + s2
printValue("3","4") #34</code></pre>
<h5>解法二</h5>
<pre><code class="python">sum = lambda s1,s2 : s1 + s2
print(sum("10","45")) # 1045</code></pre>
<h4><strong>Question 30:</strong></h4>
<blockquote><strong><em>Define a function that can accept two strings as input and print the string with maximum length in console. If two strings have the same length, then the function should print all strings line by line.</em></strong></blockquote>
<h5>解法一</h5>
<pre><code class="python">def printVal(s1,s2):
len1 = len(s1)
len2 = len(s2)
if len1 > len2:
print(s1)
elif len1 < len2:
print(s2)
else:
print(s1)
print(s2)
s1,s2=input().split()
printVal(s1,s2)</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=FGGbBZGFPvETV0FYiutcPQ%3D%3D.pxnzLAhysS3m3Vp3cGNPE7740chgO1j44Ye%2FJhG5TKfjQTumIQy%2B2v868%2BQ6W5Ky0LyeNl8HdpM7ODJiX78fpqsZoLjJ0ucPH0hchVN2UgusbrhR5ZqSgK0cED21fiasRINvTavT%2Fla5ohiik7tRPQ%3D%3D" rel="nofollow">Python 21-30题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Python基础练习100题 ( 11~ 20)
https://segmentfault.com/a/1190000019375159
2019-06-03T17:24:01+08:00
2019-06-03T17:24:01+08:00
alpha94511
https://segmentfault.com/u/alpha94511
1
<h2>刷题继续</h2>
<p>上一期和大家分享了前10道题,今天继续来刷11~20</p>
<h4><strong>Question 11:</strong></h4>
<blockquote>
<strong><em>Write a program which accepts a sequence of comma separated 4 digit binary numbers as its input and then check whether they are divisible by 5 or not. The numbers that are divisible by 5 are to be printed in a comma separated sequence.</em></strong><p><strong><em>Example:</em></strong></p>
</blockquote>
<pre><code>0100,0011,1010,1001</code></pre>
<blockquote><strong><em>Then the output should be:</em></strong></blockquote>
<pre><code>1010</code></pre>
<blockquote><strong><em>Notes: Assume the data is input by console.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code class="python">def check(x): # check function returns true if divisible by 5
return int(x,2)%5 == 0 # int(x,b) takes x as string and b as base from which
# it will be converted to decimal
data = input().split(',')
data = list(filter(check,data)) # in filter(func,object) function, elements are picked from 'data' if found True by 'check' function
print(",".join(data))</code></pre>
<h5>解法二</h5>
<pre><code class="python">value = []
items=[int(x) for x in input().split(',')]
result = " ".join(str(x) for x in items if x %5==0 )
print(",".join(result))</code></pre>
<h5>解法三</h5>
<pre><code class="python">data = input().split(',')
data = list(filter(lambda i:int(i,2)%5==0,data))
print(",".join(data))</code></pre>
<h4><strong>Question 12:</strong></h4>
<blockquote><strong><em>Write a program, which will find all such numbers between 1000 and 3000 (both included) such that each digit of the number is an even number.The numbers obtained should be printed in a comma-separated sequence on a single line.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code class="python">values = []
for i in range(1000, 3001):
s = str(i)
if (int(s[0])%2==0) and (int(s[1])%2==0) and (int(s[2])%2==0) and (int(s[3])%2==0):
values.append(s)
print (",".join(values))
</code></pre>
<h5>解法二</h5>
<pre><code class="python3">lst = []
for i in range(1000,3001):
flag = 1
for j in str(i): # every integer number i is converted into string
if ord(j)%2 != 0: # ord returns ASCII value and j is every digit of i
flag = 0 # flag becomes zero if any odd digit found
if flag == 1:
lst.append(str(i)) # i is stored in list as string
print(",".join(lst))
</code></pre>
<h5>解法三</h5>
<pre><code class="python">def check(element):
return all(ord(i)%2 == 0 for i in element) # all returns True if all digits i is even in element
lst = [str(i) for i in range(1000,3001)] # creates list of all given numbers with string data type
lst = list(filter(check,lst)) # filter removes element from list if check condition fails
print(",".join(lst))
</code></pre>
<h5>解法四</h5>
<pre><code class="python">lst = [str(i) for i in range(1000,3001)]
lst = list(filter(lambda i:all(ord(j)%2 == 0 for j in i),lst )) # using lambda to define function inside filter function
print(",".join(lst))
</code></pre>
<h4><strong>Question 13:</strong></h4>
<blockquote>
<strong><em>Write a program that accepts a sentence and calculate the number of letters and digits.</em></strong><p><strong><em>Suppose the following input is supplied to the program:</em></strong></p>
</blockquote>
<pre><code>hello world! 123</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>LETTERS 10
DIGITS 3</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">text_input = input()
d={"DIGITS":0, "LETTERS":0,'SPACE':0}
d['DIGITS']= sum(c.isdigit() for c in text_input)
d['LETTERS']= sum(c.isalpha() for c in text_input)
d['SPACE'] = sum(c.isspace() for c in text_input)
for k,v in d.items():
print(k,v)
</code></pre>
<h5>解法二</h5>
<pre><code class="python">word = input()
letter,digit = 0,0
for i in word:
if ('a'<=i and i<='z') or ('A'<=i and i<='Z'):
letter+=1
if '0'<=i and i<='9':
digit+=1
print("LETTERS {0}\nDIGITS {1}".format(letter,digit))
</code></pre>
<h5>解法三</h5>
<pre><code class="python3">word = input()
letter,digit = 0,0
for i in word:
letter+=i.isalpha() # returns True if alphabet
digit+=i.isnumeric() # returns True if numeric
print("LETTERS %d\nDIGITS %d"%(letter,digit)) # two different types of formating method is shown in both solution
</code></pre>
<h4><strong>Question 14:</strong></h4>
<blockquote>
<strong><em>Write a program that accepts a sentence and calculate the number of upper case letters and lower case letters.</em></strong><p><strong><em>Suppose the following input is supplied to the program:</em></strong></p>
</blockquote>
<pre><code>Hello world!</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>UPPER CASE 1
LOWER CASE 9</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">word = input()
upper,lower = 0,0
for i in word:
if 'a'<=i and i<='z' :
lower+=1
if 'A'<=i and i<='Z':
upper+=1
print("UPPER CASE {0}\nLOWER CASE {1}".format(upper,lower))</code></pre>
<h5>解法二</h5>
<pre><code class="python">
text_input = input()
d={"UPPER CASE":0, "LOWER CASE":0}
for c in text_input:
if c.isupper():
d["UPPER CASE"]+=1
elif c.islower():
d["LOWER CASE"]+=1
else:
pass
print ("UPPER CASE", d["UPPER CASE"])
print ("LOWER CASE", d["LOWER CASE"])
</code></pre>
<h5>解法三</h5>
<pre><code class="python3">
word = input()
upper = sum(1 for i in word if i.isupper())
lower = sum(1 for i in word if i.islower())
print("UPPER CASE {0}\nLOWER CASE {1}".format(upper,lower))</code></pre>
<h4><strong>Question 15:</strong></h4>
<blockquote>
<strong><em>Write a program that computes the value of a+aa+aaa+aaaa with a given digit as the value of a.</em></strong><p><strong><em>Suppose the following input is supplied to the program:</em></strong></p>
</blockquote>
<pre><code>9</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>11106</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">a = input()
total,tmp = 0,str() # initialing an integer and empty string
for i in range(4):
tmp+=a # concatenating 'a' to 'tmp'
total+=int(tmp) # converting string type to integer type
print(total)
</code></pre>
<h5>解法二</h5>
<pre><code class="python3">a = input()
total = int(a) + int(2*a) + int(3*a) + int(4*a) # N*a=Na, for example a="23", 2*a="2323",3*a="232323"
print(total)
</code></pre>
<h4><strong>Question 16:</strong></h4>
<blockquote>
<strong><em>Use a list comprehension to square each odd number in a list. The list is input by a sequence of comma-separated numbers.</em></strong><br><strong><em>Suppose the following input is supplied to the program:</em></strong>
</blockquote>
<pre><code>1,2,3,4,5,6,7,8,9</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>1,3,5,7,9</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">values = input()
numbers = [x for x in values.split(",") if int(x)%2!=0]
print (",".join(numbers))
</code></pre>
<h4><strong>Question 17:</strong></h4>
<blockquote><strong><em>Write a program that computes the net amount of a bank account based a transaction log from console input. The transaction log format is shown as following:</em></strong></blockquote>
<pre><code>D 100
W 200</code></pre>
<ul><li>D means deposit while W means withdrawal.</li></ul>
<blockquote><strong><em>Suppose the following input is supplied to the program:</em></strong></blockquote>
<pre><code>D 300
D 300
W 200
D 100</code></pre>
<blockquote><strong><em>Then, the output should be:</em></strong></blockquote>
<pre><code>500</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">
netAmount = 0
while True:
s = input()
if not s:
break
values = s.split(" ")
operation = values[0]
amount = int(values[1])
if operation=="D":
netAmount+=amount
elif operation=="W":
netAmount-=amount
else:
pass
print(netAmount)
</code></pre>
<h5>解法二</h5>
<pre><code class="python">total = 0
while True:
s = input().split()
if not s: # break if the string is empty
break
cm,num = map(str,s)
if cm=='D':
total+=int(num)
if cm=='W':
total-=int(num)
print(total)</code></pre>
<h4><strong>Question 18:</strong></h4>
<blockquote>
<strong><em>A website requires the users to input username and password to register. Write a program to check the validity of password input by users.</em></strong><p><strong><em>Following are the criteria for checking the password:</em></strong></p>
</blockquote>
<ul>
<li><strong><em>At least 1 letter between [a-z]</em></strong></li>
<li><strong><em>At least 1 number between [0-9]</em></strong></li>
<li><strong><em>At least 1 letter between [A-Z]</em></strong></li>
<li><strong><em>At least 1 character from [$#@]</em></strong></li>
<li><strong><em>Minimum length of transaction password: 6</em></strong></li>
<li><strong><em>Maximum length of transaction password: 12</em></strong></li>
</ul>
<blockquote>
<strong><em>Your program should accept a sequence of comma separated passwords and will check them according to the above criteria. Passwords that match the criteria are to be printed, each separated by a comma.</em></strong><p><strong><em>Example</em></strong></p>
<p><strong><em>If the following passwords are given as input to the program:</em></strong></p>
</blockquote>
<pre><code>ABd1234@1,a F1#,2w3E*,2We3345</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>ABd1234@1</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">import re
value = []
items=[x for x in input().split(',')]
for p in items:
if (len(p)<6 or len(p)>12):
break
elif not re.search("[a-z]",p):
break
elif not re.search("[0-9]",p):
break
elif not re.search("[A-Z]",p):
break
elif not re.search("[$#@]",p):
break
elif re.search("\s",p):
break
else:
value.append(p)
break
print (",".join(value))</code></pre>
<h5>解法二</h5>
<pre><code class="python">def is_low(x): # Returns True if the string has a lowercase
for i in x:
if 'a'<=i and i<='z':
return True
return False
def is_up(x): # Returns True if the string has a uppercase
for i in x:
if 'A'<= i and i<='Z':
return True
return False
def is_num(x): # Returns True if the string has a numeric digit
for i in x:
if '0'<=i and i<='9':
return True
return False
def is_other(x): # Returns True if the string has any "$#@"
for i in x:
if i=='$' or i=='#' or i=='@':
return True
return False
s = input().split(',')
lst = []
for i in s:
length = len(i)
if 6 <= length and length <= 12 and is_low(i) and is_up(i) and is_num(i) and is_other(i): #Checks if all the requirments are fulfilled
lst.append(i)
print(",".join(lst))
</code></pre>
<h5>解法三</h5>
<pre><code class="python">def check(x):
cnt = (6<=len(x) and len(x)<=12)
for i in x:
if i.isupper():
cnt+=1
break
for i in x:
if i.islower():
cnt+=1
break
for i in x:
if i.isnumeric():
cnt+=1
break
for i in x:
if i=='@' or i=='#'or i=='$':
cnt+=1
break
return cnt == 5 # counting if total 5 all conditions are fulfilled then returns True
s = input().split(',')
lst = filter(check,s) # Filter function pick the words from s, those returns True by check() function
print(",".join(lst))
</code></pre>
<h5>解法四</h5>
<pre><code class="python">import re
s = input().split(',')
lst = []
for i in s:
cnt = 0
cnt+=(6<=len(i) and len(i)<=12)
cnt+=bool(re.search("[a-z]",i)) # here re module includes a function re.search() which returns the object information
cnt+=bool(re.search("[A-Z]",i)) # of where the pattern string i is matched with any of the [a-z]/[A-z]/[0=9]/[@#$] characters
cnt+=bool(re.search("[0-9]",i)) # if not a single match found then returns NONE which converts to False in boolean
cnt+=bool(re.search("[@#$]",i)) # expression otherwise True if found any.
if cnt == 5:
lst.append(i)
print(",".join(lst))
</code></pre>
<h4><strong>Question 19:</strong></h4>
<blockquote><strong><em>You are required to write a program to sort the (name, age, score) tuples by ascending order where name is string, age and score are numbers. The tuples are input by console. The sort criteria is:</em></strong></blockquote>
<ul>
<li><strong><em>1: Sort based on name</em></strong></li>
<li><strong><em>2: Then sort based on age</em></strong></li>
<li><strong><em>3: Then sort by score</em></strong></li>
</ul>
<blockquote>
<strong><em>The priority is that name > age > score.</em></strong><p><strong><em>If the following tuples are given as input to the program:</em></strong></p>
</blockquote>
<pre><code>Tom,19,80
John,20,90
Jony,17,91
Jony,17,93
Json,21,85</code></pre>
<blockquote><strong><em>Then, the output of the program should be:</em></strong></blockquote>
<pre><code>[('John', '20', '90'), ('Jony', '17', '91'), ('Jony', '17', '93'), ('Json', '21', '85'), ('Tom', '19', '80')]</code></pre>
<hr>
<h5>解法一</h5>
<pre><code class="python">lst = []
while True:
s = input().split(',')
if not s[0]: # breaks for blank input
break
lst.append(tuple(s))
lst.sort(key= lambda x:(x[0],x[1],x[2]))
print(lst)
</code></pre>
<h4><strong>Question 20:</strong></h4>
<blockquote><strong><em>Define a class with a generator which can iterate the numbers, which are divisible by 7, between a given range 0 and n.</em></strong></blockquote>
<hr>
<h5>解法一</h5>
<pre><code class="python">
class Test:
def generator(self,n):
return [i for i in range(n) if i%7==0]
n = int(input())
num = Test()
lst = num.generator(n)
print(lst)
</code></pre>
<h2>源代码下载</h2>
<p>这十道题的代码在我的github上,如果大家想看一下每道题的输出结果,可以点击以下链接下载:</p>
<ul><li><a href="https://link.segmentfault.com/?enc=SY6jT5CP4wfd0qoAt6V7hw%3D%3D.ipwv4OxGKhu7oQrvNqUW0d4ESbvVdePzBjt1OPiNFEMHbCLVC2hS5b5r2zKYhlYOl2QW8IactpzdSnRj4UmW8w79bhVLMIkzRRwFgLYM5jdFRnmsIND5NJNReJtbyaxbnl3EIu2aFcorLtE2Wfo2jw%3D%3D" rel="nofollow">Python 11-20题</a></li></ul>
<p>我的运行环境Python 3.6+,如果你用的是Python 2.7版本,绝大多数不同就体现在以下3点:</p>
<ul>
<li>raw_input()在Python3中是input()</li>
<li>print需要加括号</li>
<li>fstring可以换成.format(),或者%s,%d</li>
</ul>
<p>谢谢大家,我们下期见!希望各位朋友不要吝啬,把每道题的更高效的解法写在评论里,我们一起进步!!!</p>
Pandas之旅(七) 谁说pandas慢
https://segmentfault.com/a/1190000018770156
2019-04-05T04:41:25+08:00
2019-04-05T04:41:25+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<h2>Pandas 加速</h2>
<p>大家好,今天我们来看有关pandas加速的小技巧,不知道大家在刚刚接触pandas的时候有没有听过如下的说法</p>
<blockquote><strong><em>pandas太慢了,运行要等半天</em></strong></blockquote>
<p>其实我想说的是,慢不是pandas的错,大家要知道pandas本身是在Numpy上建立起来的包,在很多情况下是支持向量化运算的,而且还有C的底层设计,所以我今天<br>主要想从几个方面和大家分享一下pandas加速的小技巧,与往常一样,文章分成四部分,本文结构如下:</p>
<ol>
<li>使用datetime类型来处理和时间序列有关的数据</li>
<li>批量计算的技巧</li>
<li>通过HDFStore存储数据节省时间</li>
<li>源码,相关数据及GitHub地址</li>
</ol>
<p>现在就让我们开始吧</p>
<h2>1. 使用datetime类型来处理和时间序列有关的数据</h2>
<p>首先这里我们使用的数据源是一个电力消耗情况的数据(energy_cost.csv),非常贴近生活而且也是和时间息息相关的,用来做测试在合适不过了,这个csv文件大家可以在第四部分找到下载的地方哈</p>
<pre><code class="python">import os
# 这两行仅仅是切换路径,方便我上传Github,大家不用理会,只要确认csv文件和py文件再一起就行啦
os.chdir("F:\\Python教程\\segmentfault\\pandas_share\\Pandas之旅_07 谁说pandas慢")</code></pre>
<p>现在让我们看看数据大概长什么样子</p>
<pre><code class="python">import numpy as np
import pandas as pd
f"Using {pd.__name__},{pd.__version__}"</code></pre>
<pre><code>'Using pandas,0.23.0'
</code></pre>
<pre><code class="python">df = pd.read_csv('energy_cost.csv',sep=',')
df.head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>date_time</th>
<th>energy_kwh</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>2001/1/13 0:00</td>
<td>0.586</td>
</tr>
<tr>
<th>1</th>
<td>2001/1/13 1:00</td>
<td>0.580</td>
</tr>
<tr>
<th>2</th>
<td>2001/1/13 2:00</td>
<td>0.572</td>
</tr>
<tr>
<th>3</th>
<td>2001/1/13 3:00</td>
<td>0.596</td>
</tr>
<tr>
<th>4</th>
<td>2001/1/13 4:00</td>
<td>0.592</td>
</tr>
</tbody>
</table>
<p>现在我们看到初始数据的样子了,主要有date_time和energy_kwh这两列,来表示时间和消耗的电力,比较好理解,下面让我们来看一下数据类型</p>
<pre><code class="python">df.dtypes
>>> date_time object
energy_kwh float64
dtype: object</code></pre>
<pre><code class="python">type(df.iat[0,0])
>>> str</code></pre>
<p>这里有个小问题,Pandas和NumPy有dtypes(数据类型)的概念。如果未指定参数,则date_time这一列的数据类型默认object,所以为了之后运算方便,我们可以把str类型的这一列转化为timestamp类型:</p>
<pre><code class="python">df['date_time'] = pd.to_datetime(df['date_time'])
df.dtypes
>>> date_time datetime64[ns]
energy_kwh float64
dtype: object</code></pre>
<p>先在大家可以发现我们通过用pd.to_datetime这个方法已经成功的把date_time这一列转化为了datetime64类型</p>
<pre><code class="python">df.head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>date_time</th>
<th>energy_kwh</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>2001-01-13 00:00:00</td>
<td>0.586</td>
</tr>
<tr>
<th>1</th>
<td>2001-01-13 01:00:00</td>
<td>0.580</td>
</tr>
<tr>
<th>2</th>
<td>2001-01-13 02:00:00</td>
<td>0.572</td>
</tr>
<tr>
<th>3</th>
<td>2001-01-13 03:00:00</td>
<td>0.596</td>
</tr>
<tr>
<th>4</th>
<td>2001-01-13 04:00:00</td>
<td>0.592</td>
</tr>
</tbody>
</table>
<p>现在再来看数据, 发现已经和刚才不同了,我们还可以通过指定format参数实现一样的效果,速度上也会快一些</p>
<pre><code class="python">%%timeit -n 10
def convert_with_format(df, column_name):
return pd.to_datetime(df[column_name],format='%Y/%m/%d %H:%M')
df['date_time']=convert_with_format(df, 'date_time')
>>>722 µs ± 334 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)</code></pre>
<p>有关具体的日期自定义相关方法,大家点击<a href="https://link.segmentfault.com/?enc=DNm0j7ZyN5Ve0hgpkrrvPQ%3D%3D.MPVK7ZyswOxX96eL8SZf7hDYprR1ZBCd4Mi9NTuQWls%3D" rel="nofollow">这里</a>查看</p>
<h2>2. 批量计算的技巧</h2>
<p>首先,我们假设根据用电的时间段不同,电费价目表如下:</p>
<table>
<thead><tr>
<th>Type</th>
<th>cents/kwh</th>
<th>periode</th>
</tr></thead>
<tbody>
<tr>
<td>Peak</td>
<td>28</td>
<td>17:00 to 24:00</td>
</tr>
<tr>
<td>Shoulder</td>
<td>20</td>
<td>7:00 to 17:00</td>
</tr>
<tr>
<td>Off-Peak</td>
<td>12</td>
<td>0:00 to 7:00</td>
</tr>
</tbody>
</table>
<p>假设我们想要计算出电费,我们可以先写出一个根据时间动态计算电费的方法“apply_tariff“</p>
<pre><code class="python">def apply_tariff(kwh, hour):
"""Calculates cost of electricity for given hour."""
if 0 <= hour < 7:
rate = 12
elif 7 <= hour < 17:
rate = 20
elif 17 <= hour < 24:
rate = 28
else:
raise ValueError(f'Invalid hour: {hour}')
return rate * kwh</code></pre>
<p>好啦,现在我们想要在数据中新增一列 'cost_cents' 来表示总价钱,我们有很多选择,首先能想到的方法便是iterrows(),它可以让我们循环遍历Dataframe的每一行,根据条件计算并赋值给新增的‘cost_cents’列</p>
<h4><strong><em>iterrows()</em></strong></h4>
<p>首先我们能做的是循环遍历流程,让我们先用.iterrows()替代上面的方法来试试:</p>
<pre><code class="python">%%timeit -n 10
def apply_tariff_iterrows(df):
energy_cost_list = []
for index, row in df.iterrows():
# Get electricity used and hour of day
energy_used = row['energy_kwh']
hour = row['date_time'].hour
# Append cost list
energy_cost = apply_tariff(energy_used, hour)
energy_cost_list.append(energy_cost)
df['cost_cents'] = energy_cost_list
apply_tariff_iterrows(df)</code></pre>
<pre><code>983 ms ± 65.5 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
</code></pre>
<p>我们为了测试方便,所有的方法都会循环10次来比较耗时,这里很明显我们有很大的改进空间,下面我们用apply方法来优化</p>
<h4><strong><em>apply()</em></strong></h4>
<pre><code class="python">%%timeit -n 10
def apply_tariff_withapply(df):
df['cost_cents'] = df.apply(
lambda row: apply_tariff(
kwh=row['energy_kwh'],
hour=row['date_time'].hour),
axis=1)
apply_tariff_withapply(df)</code></pre>
<pre><code>247 ms ± 24.3 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
</code></pre>
<p>这回速度得到了很大的提升,但是显然我们还没有get到pandas加速的精髓:矢量化操作。下面让我们开始提速</p>
<h4><strong>isin()</strong></h4>
<p>假设我们现在的电价是定值,不根据用电时间段来改变,那么pandas中最快的方法那就是采用(df['cost_cents'] = df['energy_kwh'] * price),这就是一个简单的矢量化操作示范。它基本是在Pandas中运行最快的方式。</p>
<p>目前的问题是我们的价格是动态的,那么如何将条件判断添加到Pandas中的矢量化运算中呢?答案就是我们根据条件选择和分组DataFrame,然后对每个选定的组应用矢量化操作:</p>
<pre><code class="python">#先让我们把时间序列作为索引
df.set_index('date_time', inplace=True)</code></pre>
<pre><code class="python">%%timeit -n 10
def apply_tariff_isin(df):
# Define hour range Boolean arrays
peak_hours = df.index.hour.isin(range(17, 24))
shoulder_hours = df.index.hour.isin(range(7, 17))
off_peak_hours = df.index.hour.isin(range(0, 7))
# Apply tariffs to hour ranges
df.loc[peak_hours, 'cost_cents'] = df.loc[peak_hours, 'energy_kwh'] * 28
df.loc[shoulder_hours,'cost_cents'] = df.loc[shoulder_hours, 'energy_kwh'] * 20
df.loc[off_peak_hours,'cost_cents'] = df.loc[off_peak_hours, 'energy_kwh'] * 12
apply_tariff_isin(df)</code></pre>
<pre><code>5.7 ms ± 871 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
</code></pre>
<p>这回我们发现速度是真正起飞了,首先我们根据用电的三个时段把df进行分三组,再依次进行三次矢量化操作,大家可以发现最后减少了很多时间,原理很简单:</p>
<p>在运行的时候,.isin()方法返回一个布尔值数组,如下所示:</p>
<ul><li>[False, False, False, ..., True, True, True]</li></ul>
<p>接下来布尔数组传递给DataFrame的.loc索引器时,我们获得一个仅包含与3个用电时段匹配DataFrame切片。然后简单的进行乘法操作就行了,这样做的好处是我们已经不需要刚才提过的apply方法了,因为不在存在遍历所有行的问题</p>
<h4>我们可以做的更好吗?</h4>
<p>通过观察可以发现,在apply_tariff_isin()中,我们仍然在通过调用df.loc和df.index.hour.isin()来进行一些“手动工作”。如果想要进一步提速,我们可以使用cut方法</p>
<pre><code class="python">%%timeit -n 10
def apply_tariff_cut(df):
cents_per_kwh = pd.cut(x=df.index.hour,
bins=[0, 7, 17, 24],
include_lowest=True,
labels=[12, 20, 28]).astype(int)
df['cost_cents'] = cents_per_kwh * df['energy_kwh']</code></pre>
<pre><code>140 ns ± 29.9 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
</code></pre>
<p>效果依然锋利,速度上有了成倍的提升</p>
<h4>不要忘了用Numpy</h4>
<p>众所周知,Pandas是在Numpy上建立起来的,所以在Numpy中当然有类似cut的方法可以实现分组,从速度上来讲差不太多</p>
<pre><code class="python">%%timeit -n 10
def apply_tariff_digitize(df):
prices = np.array([12, 20, 28])
bins = np.digitize(df.index.hour.values, bins=[7, 17, 24])
df['cost_cents'] = prices[bins] * df['energy_kwh'].values
</code></pre>
<pre><code>54.9 ns ± 19.3 ns per loop (mean ± std. dev. of 7 runs, 10 loops each)
</code></pre>
<p>正常情况下,以上的加速方法是能满足日常需要的,如果有特殊的需求,大家可以上网看看有没有相关的第三方加速包</p>
<h2>3. 通过HDFStore存储数据节省时间</h2>
<p>这里主要想强调的是节省预处理的时间,假设我们辛辛苦苦搭建了一些模型,但是每次运行之前都要进行一些预处理,比如类型转换,用时间序列做索引等,如果不用HDFStore的话每次都会花去不少时间,这里Python提供了一种解决方案,可以把经过预处理的数据存储为HDF5格式,方便我们下次运行时直接调用。</p>
<p>下面就让我们把本篇文章的df通过HDF5来存储一下:</p>
<pre><code class="python"># Create storage object with filename `processed_data`
data_store = pd.HDFStore('processed_data.h5')
# Put DataFrame into the object setting the key as 'preprocessed_df'
data_store['preprocessed_df'] = df
data_store.close()</code></pre>
<p>现在我们可以关机下班了,当明天接着上班后,通过key("preprocessed_df")就可以直接使用经过预处理的数据了</p>
<pre><code class="python"># Access data store
data_store = pd.HDFStore('processed_data.h5')
# Retrieve data using key
preprocessed_df = data_store['preprocessed_df']
data_store.close()</code></pre>
<pre><code class="python">preprocessed_df.head()</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>energy_kwh</th>
<th>cost_cents</th>
</tr>
<tr>
<th>date_time</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>2001-01-13 00:00:00</th>
<td>0.586</td>
<td>7.032</td>
</tr>
<tr>
<th>2001-01-13 01:00:00</th>
<td>0.580</td>
<td>6.960</td>
</tr>
<tr>
<th>2001-01-13 02:00:00</th>
<td>0.572</td>
<td>6.864</td>
</tr>
<tr>
<th>2001-01-13 03:00:00</th>
<td>0.596</td>
<td>7.152</td>
</tr>
<tr>
<th>2001-01-13 04:00:00</th>
<td>0.592</td>
<td>7.104</td>
</tr>
</tbody>
</table>
<p>如上图所示,现在我们可以发现date_time已经是处理为index了</p>
<h2>4. 源码,相关数据及GitHub地址</h2>
<p>这一期为大家分享了一些pandas加速的实用技巧,希望可以帮到各位小伙伴,当然,类似的技巧还有很多,但是核心思想应该一直围绕矢量化操作上,毕竟是基于Numpy上建立的包,如果大家有更好的办法,希望可以在我的文章底下留言哈</p>
<p>我把这一期的ipynb文件,py文件以及我们用到的energy_cost.csv放到了Github上,大家可以点击下面的链接来下载:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=e6%2BJv%2FYk98FegQFgNvCHtg%3D%3D.1bm8LsyOS6IDVL0BN6co1OcKWvGgQ%2BSGL5ZqFvqG1jxdPLeTEVCZ77f%2FNfl4IVSFaLM9HjqyMzUFKqbHfQkswNKUOS2gvipsiAuDuS1g9dwYCnFla8ULLHs0iixzpfx6r4UIUHshG%2Fd6ff0%2Fo77MiCcNq1finhoGqN%2BlhRAeb88oVP4mm%2BMub6ltB3fkGVkN" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>希望大家能够继续支持我,这一篇文章已经是Pandas系列的最后一篇了,虽然一共只写了7篇文章,但是我认为从实用性上来讲并没有太逊色于收费课程(除了少了很多漂亮的ppt),接下来我会再接再厉,分享一下我对R (ggplot2)或者matplotlib的学习经验!!</p>
<p>Pandas之旅到此结束。撒花</p>
Pandas之旅(六): 字符串实用方法汇总
https://segmentfault.com/a/1190000018681927
2019-03-28T03:46:02+08:00
2019-03-28T03:46:02+08:00
alpha94511
https://segmentfault.com/u/alpha94511
3
<h2>有关字符串基本方法</h2>
<p>大家好,我又回来了! 之前的几期我们已经简单了解了pandas的基础操作,但是只要涉及到数据,最常见的就是String(字符串)类型,所以很多时候我们其实都在和字符串打交道,所以今天,我会把我自己总结的,有关字符串的常用方法分享给大家,希望能够帮到各位小伙伴~</p>
<h4>Split and format</h4>
<pre><code class="python">latitude = '37.24N'
longitude = '-115.81W'
'Coordinates {0},{1}'.format(latitude,longitude)
>>> 'Coordinates 37.24N,-115.81W'</code></pre>
<pre><code class="python">f'Coordinates {latitude},{longitude}'
>>>'Coordinates 37.24N,-115.81W'</code></pre>
<pre><code class="python">'{0},{1},{2}'.format(*('abc'))
>>>'a,b,c'</code></pre>
<pre><code class="python">coord = {"latitude":latitude,"longitude":longitude}
'Coordinates {latitude},{longitude}'.format(**coord)
>>>'Coordinates 37.24N,-115.81W'</code></pre>
<h4><strong>Access argument' s attribute</strong></h4>
<pre><code class="python">class Point:
def __init__(self,x,y):
self.x,self.y = x,y
def __str__(self):
return 'Point({self.x},{self.y})'.format(self = self)
def __repr__(self):
return f'Point({self.x},{self.y})'</code></pre>
<pre><code class="python">test_point = Point(4,2)
test_point
>>> Point(4,2)</code></pre>
<pre><code class="python">str(Point(4,2))
>>>'Point(4,2)'</code></pre>
<h4>
<strong>Replace with %s , %r</strong> :</h4>
<pre><code class="python">" repr() shows the quote {!r}, while str() doesn't:{!s} ".format('a1','a2')
>>> " repr() shows the quote 'a1', while str() doesn't:a2 "
</code></pre>
<h4>
<strong>Align</strong> :</h4>
<pre><code class="python">'{:<30}'.format('left aligned')
>>>'left aligned '</code></pre>
<pre><code class="python">'{:>30}'.format('right aligned')
>>>' right aligned'</code></pre>
<pre><code class="python">'{:^30}'.format('centerd')
>>>' centerd '</code></pre>
<pre><code class="python">'{:*^30}'.format('centerd')
>>>'***********centerd************'</code></pre>
<h4>
<strong>Replace with %x , %o</strong> :</h4>
<pre><code class="python">"int:{0:d}, hex:{0:x}, oct:{0:o}, bin:{0:b}".format(42)
>>>'int:42, hex:2a, oct:52, bin:101010'</code></pre>
<pre><code class="python">'{:,}'.format(12345677)
>>>'12,345,677'</code></pre>
<h4>
<strong>Percentage</strong> :</h4>
<pre><code class="python">points = 19
total = 22
'Correct answers: {:.2%}'.format(points/total)
>>>'Correct answers: 86.36%'</code></pre>
<h4>
<strong>Date</strong> :</h4>
<pre><code class="python">import datetime as dt
f"{dt.datetime.now():%Y-%m-%d}"
>>>'2019-03-27'</code></pre>
<pre><code class="python">f"{dt.datetime.now():%d_%m_%Y}"
>>>'27_03_2019'</code></pre>
<pre><code class="python">today = dt.datetime.today().strftime("%d_%m_%Y")
today</code></pre>
<pre><code>'27_03_2019'
</code></pre>
<h4>
<strong>Split without parameters</strong> :</h4>
<pre><code class="python">"this is a test".split()
>>>['this', 'is', 'a', 'test']</code></pre>
<h4>
<strong>Concatenate</strong> :</h4>
<pre><code class="python">'do'*2
>>>'dodo'</code></pre>
<pre><code class="python">orig_string ='Hello'
orig_string+',World'
>>>'Hello,World'</code></pre>
<pre><code class="python">full_sentence = orig_string+',World'
full_sentence
>>>'Hello,World'</code></pre>
<h4>
<strong>Check string type , slice,count,strip</strong> :</h4>
<pre><code class="python">strings = ['do','re','mi']
', '.join(strings)
>>>'do, re, mi'</code></pre>
<pre><code class="python">'z' not in 'abc'
>>> True</code></pre>
<pre><code class="python">ord('a'), ord('#')
>>> (97, 35)</code></pre>
<pre><code class="python">chr(97)
>>>'a'</code></pre>
<pre><code class="python">s = "foodbar"
s[2:5]
>>>'odb'</code></pre>
<pre><code class="python">s[:4] + s[4:]
>>>'foodbar'</code></pre>
<pre><code class="python">s[:4] + s[4:] == s
>>>True</code></pre>
<pre><code class="python">t=s[:]
id(s)
>>>1547542895336</code></pre>
<pre><code class="python">id(t)
>>>1547542895336</code></pre>
<pre><code class="python">s is t
>>>True</code></pre>
<pre><code class="python">s[0:6:2]
>>>'fob'</code></pre>
<pre><code class="python">s[5:0:-2]
>>>'ado'</code></pre>
<pre><code class="python">s = 'tomorrow is monday'
reverse_s = s[::-1]
reverse_s
>>>'yadnom si worromot'</code></pre>
<pre><code class="python">s.capitalize()
>>>'Tomorrow is monday'</code></pre>
<pre><code class="python">s.upper()
>>>'TOMORROW IS MONDAY'</code></pre>
<pre><code class="python">s.title()
>>>'Tomorrow Is Monday'</code></pre>
<pre><code class="python">s.count('o')
>>> 4</code></pre>
<pre><code class="python">"foobar".startswith('foo')
>>>True</code></pre>
<pre><code class="python">"foobar".endswith('ar')
>>>True</code></pre>
<pre><code class="python">"foobar".endswith('oob',0,4)
>>>True</code></pre>
<pre><code class="python">"foobar".endswith('oob',2,4)
>>>False</code></pre>
<pre><code class="python">"My name is yo, I work at SG".find('yo')
>>>11</code></pre>
<pre><code class="python"># If can't find the string, return -1
"My name is ya, I work at Gener".find('gent')
>>>-1
</code></pre>
<pre><code class="python"># Check a string if consists of alphanumeric characters
"abc123".isalnum()
>>>True</code></pre>
<pre><code class="python">"abc%123".isalnum()
>>>False</code></pre>
<pre><code class="python">"abcABC".isalpha()
>>>True</code></pre>
<pre><code class="python">"abcABC1".isalpha()
>>>False</code></pre>
<pre><code class="python">'123'.isdigit()
>>>True</code></pre>
<pre><code class="python">'123abc'.isdigit()
>>>False</code></pre>
<pre><code class="python">'abc'.islower()
>>>True</code></pre>
<pre><code class="python">"This Is A Title".istitle()
>>>True</code></pre>
<pre><code class="python">"This is a title".istitle()
>>>False</code></pre>
<pre><code class="python">'ABC'.isupper()
>>>True</code></pre>
<pre><code class="python">'ABC1%'.isupper()
>>>True</code></pre>
<pre><code class="python">'foo'.center(10)
>>>' foo '</code></pre>
<pre><code class="python">' foo bar baz '.strip()
>>>'foo bar baz'</code></pre>
<pre><code class="python">' foo bar baz '.lstrip()
>>>'foo bar baz '</code></pre>
<pre><code class="python">' foo bar baz '.rstrip()
>>>' foo bar baz'</code></pre>
<pre><code class="python">"foo abc foo def fo ljk ".replace('foo','yao')
>>>'yao abc yao def fo ljk '
</code></pre>
<pre><code class="python">'www.realpython.com'.strip('w.moc')
>>>'realpython'</code></pre>
<pre><code class="python">'www.realpython.com'.strip('w.com')
>>>'realpython'</code></pre>
<pre><code class="python">'www.realpython.com'.strip('w.ncom')
>>>'realpyth'</code></pre>
<h4>
<strong>Convert to lists</strong> :</h4>
<pre><code class="python">', '.join(['foo','bar','baz','qux'])
>>>'foo, bar, baz, qux'</code></pre>
<pre><code class="python">list('corge')
>>>['c', 'o', 'r', 'g', 'e']</code></pre>
<pre><code class="python">':'.join('corge')
>>>'c:o:r:g:e'</code></pre>
<pre><code class="python">'www.foo'.partition('.')
>>>('www', '.', 'foo')</code></pre>
<pre><code class="python">'foo@@bar@@baz'.partition('@@')
>>>('foo', '@@', 'bar@@baz')</code></pre>
<pre><code class="python">'foo@@bar@@baz'.rpartition('@@')
>>>('foo@@bar', '@@', 'baz')</code></pre>
<pre><code class="python">'foo.bar'.partition('@@')
>>>('foo.bar', '', '')</code></pre>
<pre><code class="python"># By default , rsplit split a string with white space
'foo bar adf yao'.rsplit()
>>>['foo', 'bar', 'adf', 'yao']</code></pre>
<pre><code class="python">'foo.bar.adf.ert'.split('.')
>>>['foo', 'bar', 'adf', 'ert']</code></pre>
<pre><code class="python">'foo\nbar\nadfa\nlko'.splitlines()
>>>['foo', 'bar', 'adfa', 'lko']</code></pre>
<h3>总结</h3>
<p>除了我以上总结的这些,还有太多非常实用的方法,大家可以根据自己的需求去搜索啦!</p>
<p>我把这一期的ipynb文件和py文件放到了Github上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=7Juy0uxyNnU2uItIrgip2g%3D%3D.7WUKZRgGnD55qAV8IdgX83eKRkGV52lqKWcBomjGx5pV4xemq%2BVfbzSRLlLJqF4jifkaSb%2FEh6w9mC3jpVXMxMYE0iJiPkIhz%2FVwIcklL%2FBBiMrjb%2BWENBnjAbdvvPD76bgbC27vS%2BLWJ9RlT26XrqU6dVMhfbUXri8KaFeaxOPX6xmb2ADlDTF2w1xJsEz7" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>希望大家能够继续支持我,完结,撒花</p>
Pandas之旅(五): 构建模型初入门:检验数据一致性
https://segmentfault.com/a/1190000018637236
2019-03-25T00:00:42+08:00
2019-03-25T00:00:42+08:00
alpha94511
https://segmentfault.com/u/alpha94511
3
<h2>Pandas 如何根据需要创建简单模型</h2>
<p>大家好,今天这一期我想和大家分享有关于pandas创建模型的部分,首先让我们来看一个比较常见的场景:</p>
<blockquote><strong>你每天需要打开N个excel进行相同的操作,各种眼花缭乱的VBA函数后老眼昏花。。。。</strong></blockquote>
<p>这种情况下,最好的解决办法是先仔细想想业务需求是什么,根据实际情况可以用pandas搭建一个小型模型,一旦搭建完毕,你每天上班时就可以愉快地运行Python脚本,转身去喝杯咖啡,几分钟后心满意足地回来,发现所有的繁琐操作已经搞定了,生活是这么美好、、、</p>
<p>闲话少说,让我今天抛砖引玉,为大家简单介绍一个我使用比较多的小模型:检验数据一致性(新老数据增加和减少的数量一致),今天的文章主要分为5部分</p>
<ol>
<li>制作假数据</li>
<li>明确模型目的</li>
<li>开始实践</li>
<li>源码及GitHub地址</li>
</ol>
<p>好啦,话不多说,让我们一个个看吧</p>
<h2>1. 制作假数据</h2>
<pre><code class="python">import os</code></pre>
<pre><code class="python">#这两行仅仅是切换路径,方便我上传Github,大家不用理会
os.chdir("F:\\Python教程\\segmentfault\\pandas_share\\Pandas之旅_05 如何构建基础模型")
os.getcwd()</code></pre>
<pre><code>'F:\\Python教程\\segmentfault\\pandas_share\\Pandas之旅_05 如何构建基础模型'
</code></pre>
<p>首先让我们一起制作一些假数据,我这里接下来生成一些有关订单的假数据,当然,到了文章的最后可能你会发现我们的模型并不是完美适用于这个类型,你会在生活中根据自己需要来调整,但是至少基础的思路已经有啦!</p>
<p>先建立一个fake_product的字典,keys是产品,value是单价,这里我们用一个在网上随便找到的商品名称的csv数据集,它只有一列ProductNames,product_names.csv和最后的代码都会放在github上,如果大家感兴趣可以下载~</p>
<pre><code class="python">import numpy as np
import pandas as pd
f"Using {pd.__name__},{pd.__version__}"</code></pre>
<pre><code>'Using pandas,0.23.0'
</code></pre>
<pre><code class="python">fake_df = pd.read_csv("product_names.csv")
fake_df.head(10)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Product_Names</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>TrailChef Deluxe Cook Set</td>
</tr>
<tr>
<th>1</th>
<td>TrailChef Double Flame</td>
</tr>
<tr>
<th>2</th>
<td>Star Dome</td>
</tr>
<tr>
<th>3</th>
<td>Star Gazer 2</td>
</tr>
<tr>
<th>4</th>
<td>Hibernator Lite</td>
</tr>
<tr>
<th>5</th>
<td>Hibernator Extreme</td>
</tr>
<tr>
<th>6</th>
<td>Hibernator Camp Cot</td>
</tr>
<tr>
<th>7</th>
<td>Firefly Lite</td>
</tr>
<tr>
<th>8</th>
<td>Firefly Extreme</td>
</tr>
<tr>
<th>9</th>
<td>EverGlow Single</td>
</tr>
</tbody>
</table>
<pre><code class="python">fake_df['Product_Names'].is_unique</code></pre>
<pre><code>True
</code></pre>
<p>这里我们可以看到,数据集主要包括的就是一些产品的名字,而且没有重复值,我们现在把他们导出至一个字典,并随机给每个产品任意的价格(在20至100之间),因为这里我们要随机生成一些假数据,所以让我们引用random这个包</p>
<pre><code class="python">import random</code></pre>
<pre><code class="python">fake_product = { k:random.randint(20,100) for k in fake_df['Product_Names']}
fake_product</code></pre>
<pre><code>{'TrailChef Deluxe Cook Set': 62,
'TrailChef Double Flame': 78,
'Star Dome': 58,
'Star Gazer 2': 73,
'Hibernator Lite': 56,
'Hibernator Extreme': 99,
'Hibernator Camp Cot': 33,
'Firefly Lite': 27,
'Firefly Extreme': 30,
'EverGlow Single': 44,
'EverGlow Butane': 33,
'Husky Rope 50': 59,
'Husky Rope 60': 81,
'Husky Rope 100': 71,
'Husky Rope 200': 81,
'Granite Climbing Helmet': 86,
'Husky Harness': 76,
'Husky Harness Extreme': 73,
'Granite Signal Mirror': 67,
'Granite Carabiner': 63,
'Granite Belay': 49,
'Granite Pulley': 48,
'Firefly Climbing Lamp': 47,
'Firefly Charger': 60,
'Firefly Rechargeable Battery': 52,
'Granite Chalk Bag': 22,
'Granite Ice': 71,
'Granite Hammer': 50,
'Granite Shovel': 41,
'Granite Grip': 74,
'Granite Axe': 68,
'Granite Extreme': 74,
'Mountain Man Extreme': 87,
'Polar Sun': 82,
'Polar Ice': 47,
'Edge Extreme': 53,
'Bear Survival Edge': 81,
'Glacier GPS Extreme': 48,
'BugShield Extreme': 87,
'Sun Shelter Stick': 42,
'Compact Relief Kit': 46,
'Aloe Relief': 24,
'Infinity': 73,
'TX': 43,
'Legend': 100,
'Kodiak': 44,
'Capri': 31,
'Cat Eye': 62,
'Dante': 71,
'Fairway': 77,
'Inferno': 59,
'Maximus': 38,
'Trendi': 35,
'Zone': 87,
'Max Gizmo': 67,
'Pocket Gizmo': 73,
'Ranger Vision': 73,
'Trail Master': 96,
'Hailstorm Steel Irons': 79,
'Hailstorm Titanium Irons': 31,
'Lady Hailstorm Steel Irons': 91,
'Lady Hailstorm Titanium Irons': 99,
'Hailstorm Titanium Woods Set': 74,
'Hailstorm Steel Woods Set': 30,
'Lady Hailstorm Titanium Woods Set': 99,
'Lady Hailstorm Steel Woods Set': 84,
'Course Pro Putter': 64,
'Blue Steel Putter': 26,
'Blue Steel Max Putter': 96,
'Course Pro Golf and Tee Set': 90,
'Course Pro Umbrella': 20,
'Course Pro Golf Bag': 66,
'Course Pro Gloves': 61,
'TrailChef Canteen': 60,
'TrailChef Kitchen Kit': 53,
'TrailChef Cup': 88,
'TrailChef Cook Set': 27,
'TrailChef Single Flame': 45,
'TrailChef Kettle': 70,
'TrailChef Utensils': 88,
'Star Gazer 6': 42,
'Star Peg': 28,
'Hibernator': 47,
'Hibernator Self - Inflating Mat': 66,
'Hibernator Pad': 89,
'Hibernator Pillow': 84,
'Canyon Mule Climber Backpack': 82,
'Canyon Mule Weekender Backpack': 92,
'Canyon Mule Journey Backpack': 82,
'Canyon Mule Cooler': 23,
'Canyon Mule Carryall': 56,
'Firefly Mapreader': 77,
'Firefly 2': 76,
'Firefly 4': 75,
'Firefly Multi-light': 91,
'EverGlow Double': 34,
'EverGlow Lamp': 28,
'Mountain Man Analog': 39,
'Mountain Man Digital': 85,
'Mountain Man Deluxe': 84,
'Mountain Man Combination': 40,
'Venue': 56,
'Lux': 44,
'Polar Sports': 20,
'Polar Wave': 62,
'Bella': 45,
'Hawk Eye': 42,
'Seeker 35': 81,
'Seeker 50': 90,
'Opera Vision': 98,
'Glacier Basic': 63,
'Glacier GPS': 66,
'Trail Scout': 32,
'BugShield Spray': 34,
'BugShield Lotion Lite': 90,
'BugShield Lotion': 84,
'Sun Blocker': 88,
'Sun Shelter 15': 45,
'Sun Shelter 30': 100,
'Sun Shield': 62,
'Deluxe Family Relief Kit': 43,
'Calamine Relief': 82,
'Insect Bite Relief': 72,
'Star Lite': 32,
'Star Gazer 3': 95,
'Single Edge': 87,
'Double Edge': 20,
'Bear Edge': 80,
'Glacier Deluxe': 82,
'BugShield Natural': 83,
'TrailChef Water Bag': 99,
'Canyon Mule Extreme Backpack': 58,
'EverGlow Kerosene': 78,
'Sam': 67,
'Polar Extreme': 34,
'Seeker Extreme': 43,
'Seeker Mini': 26,
'Flicker Lantern': 44,
'Trail Star': 47,
'Zodiak': 31,
'Sky Pilot': 58,
'Retro': 99,
'Astro Pilot': 99,
'Auto Pilot': 20}
</code></pre>
<pre><code class="python">len(fake_product)</code></pre>
<pre><code>144
</code></pre>
<p>这里我们看到生成了一个有144个item组成,key为产品名称,value及单价的fake_product字典,接下来为了省事,<br>我简单地创建了一个方法get_fake_data可以让我们最终得到一个填充好的假数据集合,返回的也是字典</p>
<pre><code class="python">def get_fake_data(id_range_start,id_range_end,random_quantity_range=50):
# Id=["A00"+str(i) for i in range(0,id_range)]
Id=[]
Quantity = []
Product_name=[]
Unit_price=[]
Total_price=[]
for i in range(id_range_start,id_range_end):
random_quantity = random.randint(1,random_quantity_range)
name, price = random.choice(list(fake_product.items()))
Id.append("A00"+str(i))
Quantity.append(random_quantity)
Product_name.append(name)
Unit_price.append(price)
Total_price.append(price*random_quantity)
result = {
'Product_ID':Id,
'Product_Name':Product_name,
'Quantity':Quantity,
'Unit_price':Unit_price,
'Total_price':Total_price
}
return result
# total = [quantity[i]* v for i,v in enumerate(unit_price)] 也可以最后用推导式来求total,皮一下
# total_price=[q*p for q in quantity for p in unit_price]
</code></pre>
<p>首先,这个方法不够简洁,大家可以优化一下,但是今天的重点在于小模型,让我们着重看一下最后返回的dict,它包含如下几列:</p>
<ul>
<li>Product_ID:订单号,按照顺序递增生成</li>
<li>Product_Name:产品名称,随机生成</li>
<li>Quantity:随机生成在1~random_quantity_range之间的每个订单的产品订购量</li>
<li>Unit_price:产品价格</li>
<li>Total_price:总价</li>
</ul>
<p>每组数据长度均为 id_range_end - id_range_start,现在让我们生成两组假数据:</p>
<pre><code class="python">fake_data= get_fake_data(1,len(fake_product)+1)</code></pre>
<p>这里我们可以看到我们生成了一组假数据,Id从A001 ~ A00145</p>
<p>让我们简单看看假数据的keys和每组数据的长度:</p>
<pre><code class="python">fake_data.keys()</code></pre>
<pre><code>dict_keys(['Product_ID', 'Product_Name', 'Quantity', 'Unit_price', 'Total_price'])
</code></pre>
<pre><code class="python">for v in fake_data.values():
print(len(v))</code></pre>
<pre><code>144
144
144
144
144
</code></pre>
<p>可以发现每组key对应的list长度都是144</p>
<h2>2. 明确模型的目的</h2>
<p>我们可以利用pandas自带的from_dict方法把dict转化为Dataframe,这里我们分别用刚刚生成的fake_data来模拟1月的库存和2月的库存情况,我们可以把fake_data分成两组,A001-A00140一组,A008-A00144一组,这样就完美的模拟了实际情况。</p>
<p>因为大多数的商品名称不会改变(8~140的部分),但是从一月到二月,因为各种原因我们减少了7个商品种类的库存(1-7),又增加了4个种类的库存(141-144),我们这里验证一致性的公式就是:</p>
<blockquote>新增的 + 一月数据总量 = 减少的 + 二月数据总量</blockquote>
<h2>3. 开始实践</h2>
<p>现在让我们来实现这个小模型,首先生成stock_jan,stock_fev两个dataframe</p>
<pre><code class="python">stock= pd.DataFrame.from_dict(fake_data)
stock.head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Product_ID</th>
<th>Product_Name</th>
<th>Quantity</th>
<th>Unit_price</th>
<th>Total_price</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>A001</td>
<td>Course Pro Golf Bag</td>
<td>39</td>
<td>66</td>
<td>2574</td>
</tr>
<tr>
<th>1</th>
<td>A002</td>
<td>EverGlow Kerosene</td>
<td>18</td>
<td>78</td>
<td>1404</td>
</tr>
<tr>
<th>2</th>
<td>A003</td>
<td>Lux</td>
<td>24</td>
<td>44</td>
<td>1056</td>
</tr>
<tr>
<th>3</th>
<td>A004</td>
<td>Course Pro Putter</td>
<td>12</td>
<td>64</td>
<td>768</td>
</tr>
<tr>
<th>4</th>
<td>A005</td>
<td>Seeker 50</td>
<td>42</td>
<td>90</td>
<td>3780</td>
</tr>
</tbody>
</table>
<pre><code class="python">stock.set_index(stock['Product_ID'],inplace=True)
stock.drop('Product_ID',axis=1,inplace=True)
stock.head()</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Product_Name</th>
<th>Quantity</th>
<th>Unit_price</th>
<th>Total_price</th>
</tr>
<tr>
<th>Product_ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A001</th>
<td>Course Pro Golf Bag</td>
<td>39</td>
<td>66</td>
<td>2574</td>
</tr>
<tr>
<th>A002</th>
<td>EverGlow Kerosene</td>
<td>18</td>
<td>78</td>
<td>1404</td>
</tr>
<tr>
<th>A003</th>
<td>Lux</td>
<td>24</td>
<td>44</td>
<td>1056</td>
</tr>
<tr>
<th>A004</th>
<td>Course Pro Putter</td>
<td>12</td>
<td>64</td>
<td>768</td>
</tr>
<tr>
<th>A005</th>
<td>Seeker 50</td>
<td>42</td>
<td>90</td>
<td>3780</td>
</tr>
</tbody>
</table>
<pre><code class="python"># 获得1月份stock数据,A001-A00140
stock_jan=stock[:'A00140']
stock_jan.tail()</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Product_Name</th>
<th>Quantity</th>
<th>Unit_price</th>
<th>Total_price</th>
</tr>
<tr>
<th>Product_ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A00136</th>
<td>Flicker Lantern</td>
<td>1</td>
<td>44</td>
<td>44</td>
</tr>
<tr>
<th>A00137</th>
<td>BugShield Spray</td>
<td>8</td>
<td>34</td>
<td>272</td>
</tr>
<tr>
<th>A00138</th>
<td>Glacier Basic</td>
<td>25</td>
<td>63</td>
<td>1575</td>
</tr>
<tr>
<th>A00139</th>
<td>Sun Blocker</td>
<td>23</td>
<td>88</td>
<td>2024</td>
</tr>
<tr>
<th>A00140</th>
<td>Granite Carabiner</td>
<td>11</td>
<td>63</td>
<td>693</td>
</tr>
</tbody>
</table>
<pre><code class="python"># 获得2月份stock数据
stock_fev=stock['A008':]
stock_fev.tail()</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Product_Name</th>
<th>Quantity</th>
<th>Unit_price</th>
<th>Total_price</th>
</tr>
<tr>
<th>Product_ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A00140</th>
<td>Granite Carabiner</td>
<td>11</td>
<td>63</td>
<td>693</td>
</tr>
<tr>
<th>A00141</th>
<td>TrailChef Utensils</td>
<td>24</td>
<td>88</td>
<td>2112</td>
</tr>
<tr>
<th>A00142</th>
<td>TrailChef Deluxe Cook Set</td>
<td>9</td>
<td>62</td>
<td>558</td>
</tr>
<tr>
<th>A00143</th>
<td>Trail Star</td>
<td>21</td>
<td>47</td>
<td>987</td>
</tr>
<tr>
<th>A00144</th>
<td>Ranger Vision</td>
<td>19</td>
<td>73</td>
<td>1387</td>
</tr>
</tbody>
</table>
<p>现在让我们简单停顿一下,看看这两个df:</p>
<ul>
<li>stock_jan: A001 - A00140的所有数据</li>
<li>stock_fev: A008 - A00144的所有数据</li>
</ul>
<p>接下来的操作很简单,用我们上篇文章提到的merge函数,这里merge的公有列为索引Product_ID,Product_Name,使用的是outer merge</p>
<pre><code class="python">merge_keys=['Product_ID','Product_Name'] </code></pre>
<pre><code class="python">check_corehence = stock_jan.merge(stock_fev,on=merge_keys,how='outer',suffixes=("_jan","_fev"))
check_corehence.head(10)</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Product_Name</th>
<th>Quantity_jan</th>
<th>Unit_price_jan</th>
<th>Total_price_jan</th>
<th>Quantity_fev</th>
<th>Unit_price_fev</th>
<th>Total_price_fev</th>
</tr>
<tr>
<th>Product_ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A001</th>
<td>Course Pro Golf Bag</td>
<td>39.0</td>
<td>66.0</td>
<td>2574.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A002</th>
<td>EverGlow Kerosene</td>
<td>18.0</td>
<td>78.0</td>
<td>1404.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A003</th>
<td>Lux</td>
<td>24.0</td>
<td>44.0</td>
<td>1056.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A004</th>
<td>Course Pro Putter</td>
<td>12.0</td>
<td>64.0</td>
<td>768.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A005</th>
<td>Seeker 50</td>
<td>42.0</td>
<td>90.0</td>
<td>3780.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A006</th>
<td>Course Pro Golf Bag</td>
<td>27.0</td>
<td>66.0</td>
<td>1782.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A007</th>
<td>Husky Rope 100</td>
<td>3.0</td>
<td>71.0</td>
<td>213.0</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A008</th>
<td>EverGlow Double</td>
<td>18.0</td>
<td>34.0</td>
<td>612.0</td>
<td>18.0</td>
<td>34.0</td>
<td>612.0</td>
</tr>
<tr>
<th>A009</th>
<td>Opera Vision</td>
<td>30.0</td>
<td>98.0</td>
<td>2940.0</td>
<td>30.0</td>
<td>98.0</td>
<td>2940.0</td>
</tr>
<tr>
<th>A0010</th>
<td>TX</td>
<td>38.0</td>
<td>43.0</td>
<td>1634.0</td>
<td>38.0</td>
<td>43.0</td>
<td>1634.0</td>
</tr>
</tbody>
</table>
<p></div></p>
<pre><code class="python">check_corehence.tail()</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>Product_Name</th>
<th>Quantity_jan</th>
<th>Unit_price_jan</th>
<th>Total_price_jan</th>
<th>Quantity_fev</th>
<th>Unit_price_fev</th>
<th>Total_price_fev</th>
</tr>
<tr>
<th>Product_ID</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A00140</th>
<td>Granite Carabiner</td>
<td>11.0</td>
<td>63.0</td>
<td>693.0</td>
<td>11.0</td>
<td>63.0</td>
<td>693.0</td>
</tr>
<tr>
<th>A00141</th>
<td>TrailChef Utensils</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>24.0</td>
<td>88.0</td>
<td>2112.0</td>
</tr>
<tr>
<th>A00142</th>
<td>TrailChef Deluxe Cook Set</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>9.0</td>
<td>62.0</td>
<td>558.0</td>
</tr>
<tr>
<th>A00143</th>
<td>Trail Star</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>21.0</td>
<td>47.0</td>
<td>987.0</td>
</tr>
<tr>
<th>A00144</th>
<td>Ranger Vision</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>19.0</td>
<td>73.0</td>
<td>1387.0</td>
</tr>
</tbody>
</table>
<p>大家可以发现前7行正是减少的商品库存,而后4行正是二月份新增的商品库存,现在让我们分别获得减少的商品库存数据和新增的商品库存数据:</p>
<pre><code class="python">new_stock = check_corehence.loc[(check_corehence['Quantity_jan'].isnull()) & (check_corehence['Quantity_fev'].notnull())]
num_new = new_stock.shape[0]
num_new</code></pre>
<pre><code>4
</code></pre>
<pre><code class="python">remove_stock = check_corehence.loc[(check_corehence['Quantity_fev'].isnull()) & (check_corehence['Quantity_jan'].notnull())]
num_remove = remove_stock.shape[0]
num_remove</code></pre>
<pre><code>7
</code></pre>
<p>再让我们分别看看1月和2月的数据量:</p>
<pre><code class="python"># 1月数据量
num_stock_jan = stock_jan.shape[0]
num_stock_jan</code></pre>
<pre><code>140
</code></pre>
<pre><code class="python"># 2月数据量
num_stock_fev = stock_fev.shape[0]
num_stock_fev</code></pre>
<pre><code>137
</code></pre>
<p>现在让我们套入公式:</p>
<pre><code class="python">num_stock_jan + num_new</code></pre>
<pre><code>144
</code></pre>
<pre><code class="python">num_stock_fev + num_remove</code></pre>
<pre><code>144
</code></pre>
<p>结果相等,数据一致性过关!</p>
<h2>4. 源码及GitHub地址</h2>
<p>这一期为大家分享了一个简单的pandas检验数据一致性的模型,模型还是非常初级阶段,功能非常简单,但是基础的搭建流程想必大家已经熟悉了,接下来小伙伴们可以根据业务需求搭建自己的模型啦,只要你每天和Excel打交道,总有一款模型适合你</p>
<p>我把这一期的ipynb文件和py文件,以及用到的商品目录Category List放到了Github上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=GFqROmoP1uZ%2BBxO8Fpl01A%3D%3D.P9Z4jzk4Sc7lvlYXIZuRhHwFMUchrKKcmZmMmHfhblHXJ16T8EyfOyJCeojnoCNvBqxM7f8JGc7PKTbgU1vqd3DOXSkxzvEoyo%2Fn9qpU%2BU2UHJR5PoFkRc7nX10u2ynmspHTuB8OJqt9b782YuSHHABvK3yrqGUGDD%2FPkK7qK4EjklJVmYfj8nlojXi5LkkN" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>希望大家能够继续支持我,完结,撒花</p>
Pandas之旅(四) : 可能是社区内最实用的Pandas技巧
https://segmentfault.com/a/1190000018565157
2019-03-19T03:14:38+08:00
2019-03-19T03:14:38+08:00
alpha94511
https://segmentfault.com/u/alpha94511
3
<h2>Pandas不为人知的七大实用技巧</h2>
<p>大家好,我今天勤快地回来了,这一期主要是和大家分享一些pandas的实用技巧,会在日常生活中大大提升效率,希望可以帮助到大家,还是老样子,先给大家奉上这一期的章节目录:</p>
<ol>
<li>自定义pandas选项,设置</li>
<li>实用pandas中testing模块构建测试数据</li>
<li>巧用accessor访问器</li>
<li>合并其他列拼接DatetimeIndex</li>
<li>使用分类数据(Categorical Data)节省时间和空间</li>
<li>利用Mapping巧妙实现映射</li>
<li>压缩pandas对象</li>
<li>源码及GitHub地址</li>
</ol>
<p>好啦,话不多说,让我们一个个看吧</p>
<h3><strong>1. 自定义pandas选项,设置</strong></h3>
<p>首先,大家可能不知道,pandas里面有一个方法pd.set_option(),利用它我们可以改变一些pandas中默认的核心设置,<br>从而适应我们自身的需要,开始前还是老样子,让我们先导入numpy和pandas包</p>
<pre><code class="python">import numpy as np
import pandas as pd
f'Using {pd.__name__}, Version {pd.__version__}'</code></pre>
<pre><code>'Using pandas, Version 0.23.0'
</code></pre>
<p>现在让我们编写一个start方法来实现自定义pandas设置</p>
<pre><code class="python">def start():
options = {
'display': {
'max_columns': None,
'max_colwidth': 25,
'expand_frame_repr': False, # Don't wrap to multiple pages
'max_rows': 14,
'max_seq_items': 50, # Max length of printed sequence
'precision': 4,
'show_dimensions': False
},
'mode': {
'chained_assignment': None # Controls SettingWithCopyWarning
}
}
for category, option in options.items():
for op, value in option.items():
pd.set_option(f'{category}.{op}', value) # Python 3.6+
if __name__ == '__main__':
start()
del start # Clean up namespace in the interpreter</code></pre>
<p>大家可以发现,我们在方法的最后调用了pandas的set_option方法,直接利用我们自定义的参数替代了原有的pandas参数,现在让我们测试一下:</p>
<pre><code class="python">pd.get_option('display.max_rows')
Out:14</code></pre>
<p>可以发现max_rows 已经被替换成了我们设置的14,现在用一个真实的例子,我们利用一组公开的鲍鱼各项指标的数据来实验,数据源来自机器学习平台的公开数据</p>
<pre><code class="python">url = ('https://archive.ics.uci.edu/ml/'
'machine-learning-databases/abalone/abalone.data')
cols = ['sex', 'length', 'diam', 'height', 'weight', 'rings']
abalone = pd.read_csv(url, usecols=[0, 1, 2, 3, 4, 8], names=cols)
abalone</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>sex</th>
<th>length</th>
<th>diam</th>
<th>height</th>
<th>weight</th>
<th>rings</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>M</td>
<td>0.455</td>
<td>0.365</td>
<td>0.095</td>
<td>0.5140</td>
<td>15</td>
</tr>
<tr>
<th>1</th>
<td>M</td>
<td>0.350</td>
<td>0.265</td>
<td>0.090</td>
<td>0.2255</td>
<td>7</td>
</tr>
<tr>
<th>2</th>
<td>F</td>
<td>0.530</td>
<td>0.420</td>
<td>0.135</td>
<td>0.6770</td>
<td>9</td>
</tr>
<tr>
<th>3</th>
<td>M</td>
<td>0.440</td>
<td>0.365</td>
<td>0.125</td>
<td>0.5160</td>
<td>10</td>
</tr>
<tr>
<th>4</th>
<td>I</td>
<td>0.330</td>
<td>0.255</td>
<td>0.080</td>
<td>0.2050</td>
<td>7</td>
</tr>
<tr>
<th>5</th>
<td>I</td>
<td>0.425</td>
<td>0.300</td>
<td>0.095</td>
<td>0.3515</td>
<td>8</td>
</tr>
<tr>
<th>6</th>
<td>F</td>
<td>0.530</td>
<td>0.415</td>
<td>0.150</td>
<td>0.7775</td>
<td>20</td>
</tr>
<tr>
<th>...</th>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<th>4170</th>
<td>M</td>
<td>0.550</td>
<td>0.430</td>
<td>0.130</td>
<td>0.8395</td>
<td>10</td>
</tr>
<tr>
<th>4171</th>
<td>M</td>
<td>0.560</td>
<td>0.430</td>
<td>0.155</td>
<td>0.8675</td>
<td>8</td>
</tr>
<tr>
<th>4172</th>
<td>F</td>
<td>0.565</td>
<td>0.450</td>
<td>0.165</td>
<td>0.8870</td>
<td>11</td>
</tr>
<tr>
<th>4173</th>
<td>M</td>
<td>0.590</td>
<td>0.440</td>
<td>0.135</td>
<td>0.9660</td>
<td>10</td>
</tr>
<tr>
<th>4174</th>
<td>M</td>
<td>0.600</td>
<td>0.475</td>
<td>0.205</td>
<td>1.1760</td>
<td>9</td>
</tr>
<tr>
<th>4175</th>
<td>F</td>
<td>0.625</td>
<td>0.485</td>
<td>0.150</td>
<td>1.0945</td>
<td>10</td>
</tr>
<tr>
<th>4176</th>
<td>M</td>
<td>0.710</td>
<td>0.555</td>
<td>0.195</td>
<td>1.9485</td>
<td>12</td>
</tr>
</tbody>
</table>
<p>我们可以看到,数据截断为14行,保留了小数点后4位小数作为精度,和我们刚刚设置的precision=4是一样的</p>
<h3><strong>2. 实用pandas中testing模块构建测试数据</strong></h3>
<p>通过pandas.util.testing提供的方法,我们可以很容易的通过几行代码就构建出一个简单的测试数据类型,比如我们现在构建一个DataTime类型的数据,<br>时间间隔为月:</p>
<pre><code class="python">import pandas.util.testing as tm
tm.N, tm.K = 15, 3 # 规定行和列
import numpy as np
np.random.seed(444)
tm.makeTimeDataFrame(freq='M').head() # 设置时间间隔为月
# tm.makeTimeDataFrame(freq='D').head() 设置时间间隔为天</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
</tr></thead>
<tbody>
<tr>
<th>2000-01-31</th>
<td>0.3574</td>
<td>-0.8804</td>
<td>0.2669</td>
</tr>
<tr>
<th>2000-02-29</th>
<td>0.3775</td>
<td>0.1526</td>
<td>-0.4803</td>
</tr>
<tr>
<th>2000-03-31</th>
<td>1.3823</td>
<td>0.2503</td>
<td>0.3008</td>
</tr>
<tr>
<th>2000-04-30</th>
<td>1.1755</td>
<td>0.0785</td>
<td>-0.1791</td>
</tr>
<tr>
<th>2000-05-31</th>
<td>-0.9393</td>
<td>-0.9039</td>
<td>1.1837</td>
</tr>
</tbody>
</table>
<p>瞎生成一组乱七八糟的数据:</p>
<pre><code class="python">tm.makeDataFrame().head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
</tr></thead>
<tbody>
<tr>
<th>nTLGGTiRHF</th>
<td>-0.6228</td>
<td>0.6459</td>
<td>0.1251</td>
</tr>
<tr>
<th>WPBRn9jtsR</th>
<td>-0.3187</td>
<td>-0.8091</td>
<td>1.1501</td>
</tr>
<tr>
<th>7B3wWfvuDA</th>
<td>-1.9872</td>
<td>-1.0795</td>
<td>0.2987</td>
</tr>
<tr>
<th>yJ0BTjehH1</th>
<td>0.8802</td>
<td>0.7403</td>
<td>-1.2154</td>
</tr>
<tr>
<th>0luaYUYvy1</th>
<td>-0.9320</td>
<td>1.2912</td>
<td>-0.2907</td>
</tr>
</tbody>
</table>
<p>关于可以随机生成的数据类型, 一共大概有30多种,大家如果感兴趣可以多试试:</p>
<pre><code class="python">[i for i in dir(tm) if i.startswith('make')]</code></pre>
<pre><code>['makeBoolIndex',
'makeCategoricalIndex',
'makeCustomDataframe',
'makeCustomIndex',
'makeDataFrame',
'makeDateIndex',
'makeFloatIndex',
'makeFloatSeries',
'makeIntIndex',
'makeIntervalIndex',
'makeMissingCustomDataframe',
'makeMissingDataframe',
'makeMixedDataFrame',
'makeMultiIndex',
'makeObjectSeries',
'makePanel',
'makePeriodFrame',
'makePeriodIndex',
'makePeriodPanel',
'makePeriodSeries',
'makeRangeIndex',
'makeStringIndex',
'makeStringSeries',
'makeTimeDataFrame',
'makeTimeSeries',
'makeTimedeltaIndex',
'makeUIntIndex',
'makeUnicodeIndex']
</code></pre>
<p>这样我们如果有测试的需求,会很容易地构建相对应的假数据来测试。</p>
<h3><strong>3. 巧用accessor访问器</strong></h3>
<p>accessor(访问器) 具体就是类似getter和setter,当然,Python里面不提倡存在setter和getter方法,但是这样可以便于大家理解,pandas Series类型有3类accessor:</p>
<pre><code class="python">pd.Series._accessors
Out:{'cat', 'dt', 'str'}</code></pre>
<ul>
<li>.cat用于分类数据,</li>
<li>.str用于字符串(对象)数据,</li>
<li>.dt用于类似日期时间的数据。</li>
</ul>
<p>让我们从.str开始看:假设现在我们有一些原始的城市/州/ 邮编数据作为Dataframe的一个字段:</p>
<pre><code class="python">addr = pd.Series([
'Washington, D.C. 20003',
'Brooklyn, NY 11211-1755',
'Omaha, NE 68154',
'Pittsburgh, PA 15211'
])</code></pre>
<pre><code class="python">addr.str.upper() # 因为字符串方法是矢量化的,这意味着它们在没有显式for循环的情况下对整个数组进行操作</code></pre>
<pre><code>0 WASHINGTON, D.C. 20003
1 BROOKLYN, NY 11211-1755
2 OMAHA, NE 68154
3 PITTSBURGH, PA 15211
dtype: object
</code></pre>
<pre><code class="python">addr.str.count(r'\d') # 查看邮编有几位</code></pre>
<pre><code>0 5
1 9
2 5
3 5
dtype: int64
</code></pre>
<p>如果我们想把每一行分成城市,州,邮编分开,可以用正则;</p>
<pre><code class="python">regex = (r'(?P<city>[A-Za-z ]+), ' # One or more letters
r'(?P<state>[A-Z]{2}) ' # 2 capital letters
r'(?P<zip>\d{5}(?:-\d{4})?)') # Optional 4-digit extension
addr.str.replace('.', '').str.extract(regex)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>city</th>
<th>state</th>
<th>zip</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Washington</td>
<td>DC</td>
<td>20003</td>
</tr>
<tr>
<th>1</th>
<td>Brooklyn</td>
<td>NY</td>
<td>11211-1755</td>
</tr>
<tr>
<th>2</th>
<td>Omaha</td>
<td>NE</td>
<td>68154</td>
</tr>
<tr>
<th>3</th>
<td>Pittsburgh</td>
<td>PA</td>
<td>15211</td>
</tr>
</tbody>
</table>
<p>第二个访问器.dt用于类似日期时间的数据。它其实属于Pandas的DatetimeIndex,如果在Series上调用,它首先转换为DatetimeIndex</p>
<pre><code class="python">daterng = pd.Series(pd.date_range('2018', periods=9, freq='Q')) # 时间间隔为季度
daterng</code></pre>
<pre><code>0 2018-03-31
1 2018-06-30
2 2018-09-30
3 2018-12-31
4 2019-03-31
5 2019-06-30
6 2019-09-30
7 2019-12-31
8 2020-03-31
dtype: datetime64[ns]
</code></pre>
<pre><code class="python">daterng.dt.day_name()</code></pre>
<pre><code>0 Saturday
1 Saturday
2 Sunday
3 Monday
4 Sunday
5 Sunday
6 Monday
7 Tuesday
8 Tuesday
dtype: object
</code></pre>
<pre><code class="python">daterng[daterng.dt.quarter > 2] # 查看2019年第3季度和第4季度</code></pre>
<pre><code>2 2018-09-30
3 2018-12-31
6 2019-09-30
7 2019-12-31
dtype: datetime64[ns]
</code></pre>
<pre><code class="python">daterng[daterng.dt.is_year_end] #查看年末的一天</code></pre>
<pre><code>3 2018-12-31
7 2019-12-31
dtype: datetime64[ns]
</code></pre>
<p>最后有关.cat访问器我们会在第5个技巧中提到</p>
<h3><strong>4. 合并其他列拼接DatetimeIndex</strong></h3>
<p>现在先让我们构建一个包含时间类型数据的Dataframe:</p>
<pre><code class="python">from itertools import product
datecols = ['year', 'month', 'day']
df = pd.DataFrame(list(product([2017, 2016], [1, 2], [1, 2, 3])),
columns=datecols)
df['data'] = np.random.randn(len(df))
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>year</th>
<th>month</th>
<th>day</th>
<th>data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>2017</td>
<td>1</td>
<td>1</td>
<td>-0.0767</td>
</tr>
<tr>
<th>1</th>
<td>2017</td>
<td>1</td>
<td>2</td>
<td>-1.2798</td>
</tr>
<tr>
<th>2</th>
<td>2017</td>
<td>1</td>
<td>3</td>
<td>0.4032</td>
</tr>
<tr>
<th>3</th>
<td>2017</td>
<td>2</td>
<td>1</td>
<td>1.2377</td>
</tr>
<tr>
<th>4</th>
<td>2017</td>
<td>2</td>
<td>2</td>
<td>-0.2060</td>
</tr>
<tr>
<th>5</th>
<td>2017</td>
<td>2</td>
<td>3</td>
<td>0.6187</td>
</tr>
<tr>
<th>6</th>
<td>2016</td>
<td>1</td>
<td>1</td>
<td>2.3786</td>
</tr>
<tr>
<th>7</th>
<td>2016</td>
<td>1</td>
<td>2</td>
<td>-0.4730</td>
</tr>
<tr>
<th>8</th>
<td>2016</td>
<td>1</td>
<td>3</td>
<td>-2.1505</td>
</tr>
<tr>
<th>9</th>
<td>2016</td>
<td>2</td>
<td>1</td>
<td>-0.6340</td>
</tr>
<tr>
<th>10</th>
<td>2016</td>
<td>2</td>
<td>2</td>
<td>0.7964</td>
</tr>
<tr>
<th>11</th>
<td>2016</td>
<td>2</td>
<td>3</td>
<td>0.0005</td>
</tr>
</tbody>
</table>
<p>我们可以发现year,month,day是分开的三列,我们如果想要把它们合并为完整的时间并作为df的索引,可以这么做:</p>
<pre><code class="python">df.index = pd.to_datetime(df[datecols])
df.head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>year</th>
<th>month</th>
<th>day</th>
<th>data</th>
</tr></thead>
<tbody>
<tr>
<th>2017-01-01</th>
<td>2017</td>
<td>1</td>
<td>1</td>
<td>-0.0767</td>
</tr>
<tr>
<th>2017-01-02</th>
<td>2017</td>
<td>1</td>
<td>2</td>
<td>-1.2798</td>
</tr>
<tr>
<th>2017-01-03</th>
<td>2017</td>
<td>1</td>
<td>3</td>
<td>0.4032</td>
</tr>
<tr>
<th>2017-02-01</th>
<td>2017</td>
<td>2</td>
<td>1</td>
<td>1.2377</td>
</tr>
<tr>
<th>2017-02-02</th>
<td>2017</td>
<td>2</td>
<td>2</td>
<td>-0.2060</td>
</tr>
</tbody>
</table>
<p>我们可以扔掉没用的列并把这个df压缩为Series:</p>
<pre><code class="python">df = df.drop(datecols, axis=1).squeeze()
df.head()</code></pre>
<pre><code>2017-01-01 -0.0767
2017-01-02 -1.2798
2017-01-03 0.4032
2017-02-01 1.2377
2017-02-02 -0.2060
Name: data, dtype: float64
</code></pre>
<pre><code class="python">type(df)</code></pre>
<pre><code>pandas.core.series.Series
</code></pre>
<pre><code class="python">df.index.dtype_str</code></pre>
<pre><code>'datetime64[ns]'
</code></pre>
<h3><strong>5. 使用分类数据(Categorical Data)节省时间和空间</strong></h3>
<p>刚刚我们在第3个技巧的时候提到了访问器,现在让我们来看最后一个.cat</p>
<p>pandas中Categorical这个数据类型非常强大,通过类型转换可以让我们节省变量在内存占用的空间,提高运算速度,不过有关具体的pandas加速实战,我会在<br>下一期说,现在让我们来看一个小栗子:</p>
<pre><code class="python">colors = pd.Series([
'periwinkle',
'mint green',
'burnt orange',
'periwinkle',
'burnt orange',
'rose',
'rose',
'mint green',
'rose',
'navy'
])
import sys
colors.apply(sys.getsizeof)
</code></pre>
<pre><code>0 59
1 59
2 61
3 59
4 61
5 53
6 53
7 59
8 53
9 53
dtype: int64
</code></pre>
<p>我们首先创建了一个Series,填充了各种颜色,接着查看了每个地址对应的颜色所占内存的大小</p>
<blockquote><strong>注意这里我们使用sys.getsizeof()来获取占内存大小,但是实际上空格也是占内存的,sys.getsizeof('')返回的是49bytes</strong></blockquote>
<p>接下来我们想把每种颜色用占内存更少的数字来表示(机器学习种非常常见),这样可以减少占用的内存,首先让我们创建一个mapper字典,给每一种颜色指定<br>一个数字</p>
<pre><code class="python">mapper = {v: k for k, v in enumerate(colors.unique())}
mapper</code></pre>
<pre><code>{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}
</code></pre>
<p>接着我们把刚才的colors数组转化为int类型:</p>
<pre><code class="python"># 也可以通过 pd.factorize(colors)[0] 实现
as_int = colors.map(mapper)
as_int</code></pre>
<pre><code>0 0
1 1
2 2
3 0
4 2
5 3
6 3
7 1
8 3
9 4
dtype: int64
</code></pre>
<p>再让我们看一下占用的内存:</p>
<pre><code class="python">as_int.apply(sys.getsizeof)</code></pre>
<pre><code>0 24
1 28
2 28
3 24
4 28
5 28
6 28
7 28
8 28
9 28
dtype: int64
</code></pre>
<p>现在可以观察到我们的内存占用的空间几乎是之前的一半,其实,刚刚我们做的正是模拟Categorical Data的转化原理。现在让我们直接调用一下:</p>
<pre><code class="python">colors.memory_usage(index=False, deep=True)
Out:650</code></pre>
<pre><code class="python">colors.astype('category').memory_usage(index=False, deep=True)
Out: 495</code></pre>
<p>大家可能感觉节省的空间并不是非常大对不对? 因为目前我们这个数据根本不是真实场景,我们仅仅把数据容量增加10倍,现在再让我们看看效果:</p>
<pre><code class="python">manycolors = colors.repeat(10)
len(manycolors) / manycolors.nunique() # Much greater than 2.0x
Out:20.0</code></pre>
<pre><code class="python">f"Not using category : { manycolors.memory_usage(index=False, deep=True)}"</code></pre>
<pre><code>'Not using category : 6500'
</code></pre>
<pre><code class="python">f"Using category : { manycolors.astype('category').memory_usage(index=False, deep=True)}"</code></pre>
<pre><code>'Using category : 585'
</code></pre>
<p>这回内存的占用量差距明显就出来了,现在让我们用.cat来简化一下刚刚的工作:</p>
<pre><code class="python">new_colors = colors.astype('category')
new_colors</code></pre>
<pre><code>0 periwinkle
1 mint green
2 burnt orange
3 periwinkle
4 burnt orange
5 rose
6 rose
7 mint green
8 rose
9 navy
dtype: category
Categories (5, object): [burnt orange, mint green, navy, periwinkle, rose]
</code></pre>
<pre><code class="python">new_colors.cat.categories # 可以使用.cat.categories查看代表的颜色</code></pre>
<pre><code>Index(['burnt orange', 'mint green', 'navy', 'periwinkle', 'rose'], dtype='object')
</code></pre>
<p>现在让我们查看把颜色代表的数字:</p>
<pre><code class="python">new_colors.cat.codes</code></pre>
<pre><code>0 3
1 1
2 0
3 3
4 0
5 4
6 4
7 1
8 4
9 2
dtype: int8
</code></pre>
<p>我们如果不满意顺序也可以从新排序:</p>
<pre><code class="python">new_colors.cat.reorder_categories(mapper).cat.codes</code></pre>
<pre><code>0 0
1 1
2 2
3 0
4 2
5 3
6 3
7 1
8 3
9 4
dtype: int8
</code></pre>
<p>有关cat其他的方法,我们还是可以通过遍历dir来查看:</p>
<pre><code class="python">[i for i in dir(new_colors.cat) if not i.startswith('_')]</code></pre>
<pre><code>['add_categories',
'as_ordered',
'as_unordered',
'categories',
'codes',
'ordered',
'remove_categories',
'remove_unused_categories',
'rename_categories',
'reorder_categories',
'set_categories']
</code></pre>
<blockquote>Categorical 数据通常不太灵活,比如我们不能直接在new_colors上新增一个新的颜色,要首先通过<br>.add_categories来添加</blockquote>
<pre><code class="python">ccolors.iloc[5] = 'a new color'</code></pre>
<pre><code>---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-36-1766a795336d> in <module>()
----> 1 ccolors.iloc[5] = 'a new color'
NameError: name 'ccolors' is not defined
</code></pre>
<pre><code class="python">new_colors = new_colors.cat.add_categories(['a new color'])</code></pre>
<pre><code class="python">new_colors.iloc[5] = 'a new color' # 不会报错</code></pre>
<pre><code class="python">new_colors.values # 成功添加</code></pre>
<h3><strong>6. 利用Mapping巧妙实现映射</strong></h3>
<p>假设现在我们有存贮国家的一组数据,和一组用来映射国家所对应的大洲的数据:</p>
<pre><code class="python">countries = pd.Series([
'United States',
'Canada',
'Mexico',
'Belgium',
'United Kingdom',
'Thailand'
])
groups = {
'North America': ('United States', 'Canada', 'Mexico', 'Greenland'),
'Europe': ('France', 'Germany', 'United Kingdom', 'Belgium')
}</code></pre>
<p>我们可以通过下面的方法来实现简单的映射:</p>
<pre><code class="python">from typing import Any
def membership_map(s: pd.Series, groups: dict,
fillvalue: Any=-1) -> pd.Series:
# Reverse & expand the dictionary key-value pairs
groups = {x: k for k, v in groups.items() for x in v}
return s.map(groups).fillna(fillvalue)</code></pre>
<pre><code class="python"> membership_map(countries, groups, fillvalue='other')</code></pre>
<p>很简单对不对,现在让我们看一下最关键的一行代码,groups = {x: k for k, v in groups.items() for x in v},这个是我之前提到过的字典推导式:</p>
<pre><code class="python">test = dict(enumerate(('ab', 'cd', 'xyz')))
{x: k for k, v in test.items() for x in v}</code></pre>
<h3><strong>7. 压缩pandas对象</strong></h3>
<pre><code class="python">如果你的pandas版本大于0.21.0,那么都可以直接把pandas用压缩形式写入,常见的类型有gzip, bz2, zip,这里我们直接用刚才鲍鱼的数据集:</code></pre>
<pre><code class="python">abalone.to_json('df.json.gz', orient='records',lines=True, compression='gzip') # 压缩为gz类型
abalone.to_json('df.json', orient='records', lines=True) #压缩为json</code></pre>
<pre><code class="python">import os.path
os.path.getsize('df.json') / os.path.getsize('df.json.gz') #压缩大小差了10倍,还是gz更厉害</code></pre>
<h3><strong>8. 源码及GitHub地址</strong></h3>
<p>这一期为大家总结了很多pandas实用的小技巧,希望大家喜欢</p>
<p>我把这一期的ipynb文件和py文件放到了Github上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=CIW%2B4O9axBwBBoP5KxAY4A%3D%3D.GS4DUmykcxwCNrGqDhEpUKSFRfwPlTYxF2ZZuzH75PqhLXBkRd65l93t3vS46e8N%2F4TIAhj26tQ0jSb1SpfI2RvIb%2FbgtTUvccBC2EUxPwZRJwrAHZ5aHvoIjpFHMVeR3YIz%2B%2FeD1uLhqDzrV5%2BaS6YigQpt6fGm4%2FXT2P38JQ29is%2Bs3F2jIwDnDHKBnS3I" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>这一期就到这里啦,希望大家能够继续支持我,完结,撒花</p>
Pandas之旅(三)最实用的Merge, Join,Concat方法详解
https://segmentfault.com/a/1190000018537597
2019-03-17T22:40:01+08:00
2019-03-17T22:40:01+08:00
alpha94511
https://segmentfault.com/u/alpha94511
6
<h2>Merge, Join, Concat</h2>
<p>大家好,我有回来啦,这周更新的有点慢,主要是因为我更新了个人简历哈哈,如果感兴趣的朋友可以去看看哈:</p>
<ul><li><a>我的主页</a></li></ul>
<p>个人认为还是很漂亮的~,不得不说,很多时候老外的设计能力还是很强。</p>
<p>好了,有点扯远了,这一期我想和大家分享的是pandas中最常见的几种方法,这些方法如果你学会了,某种程度上可以很好的替代Excel,这篇文章是pandas之旅的第三篇,主要会从以下几个方面和大家分享我的心得体会:</p>
<ol>
<li>Merge</li>
<li>Join</li>
<li>Concat</li>
<li>源码及GitHub地址</li>
</ol>
<p>话不多说,让我们开始今天的Pandas之旅吧!</p>
<h2>1. Merge</h2>
<p>首先merge的操作非常类似sql里面的join,实现将两个Dataframe根据一些共有的列连接起来,当然,在实际场景中,这些共有列一般是Id,<br>连接方式也丰富多样,可以选择inner(默认),left,right,outer 这几种模式,分别对应的是内连接,左连接,右连接</p>
<h3>1.1 InnerMerge (内连接)</h3>
<p>首先让我们简单的创建两个DF,分别为DataFrame1,DataFrame2,他们的公有列是key</p>
<pre><code class="python">import numpy as np
import pandas as pd
from pandas import Series, DataFrame</code></pre>
<pre><code class="python"># Let's make a dframe
dframe1 = DataFrame({'key':['X','Z','Y','Z','X','X'],'value_df1': np.arange(6)})
dframe1</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
</tr>
<tr>
<th>1</th>
<td>Z</td>
<td>1</td>
</tr>
<tr>
<th>2</th>
<td>Y</td>
<td>2</td>
</tr>
<tr>
<th>3</th>
<td>Z</td>
<td>3</td>
</tr>
<tr>
<th>4</th>
<td>X</td>
<td>4</td>
</tr>
<tr>
<th>5</th>
<td>X</td>
<td>5</td>
</tr>
</tbody>
</table>
<pre><code class="python">#Now lets make another dframe
dframe2 = DataFrame({'key':['Q','Y','Z'],'value_df2':[1,2,3]})
dframe2</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Q</td>
<td>1</td>
</tr>
<tr>
<th>1</th>
<td>Y</td>
<td>2</td>
</tr>
<tr>
<th>2</th>
<td>Z</td>
<td>3</td>
</tr>
</tbody>
</table>
<p>我们现在可以简单地使用pd.merge(dframe1,dframe2)来实现Merge功能</p>
<pre><code class="python">pd.merge(dframe1,dframe2)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Z</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<th>1</th>
<td>Z</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<th>2</th>
<td>Y</td>
<td>2</td>
<td>2</td>
</tr>
</tbody>
</table>
<p>我们现在需要注意一点,X仅仅是存在于dframe1的key,在dframe2中不存在,因此大家可以发现,当我们调用pd.merge的时候,会自动默认为inner join,<br>我们再换一种方式写一下,大家就明白了:</p>
<pre><code class="python">pd.merge(dframe1,dframe2,on='key',how='inner')</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Z</td>
<td>1</td>
<td>3</td>
</tr>
<tr>
<th>1</th>
<td>Z</td>
<td>3</td>
<td>3</td>
</tr>
<tr>
<th>2</th>
<td>Y</td>
<td>2</td>
<td>2</td>
</tr>
</tbody>
</table>
<pre><code class="python">大家可以发现结果是一样的,看到这里,对sql熟悉的朋友们已经有感觉了估计,因为实在是太像了,如果我们不通过on和how来指定
想要merge的公有列或者方式,那么pd.merge就会自动寻找到两个DataFrame的相同列并自动默认为inner join,至此,
估计大家也可以猜出其他几种模式的merge啦
</code></pre>
<h3>1.2 LeftMerge (左连接)</h3>
<pre><code class="python">现在同样的,让我们看一下how='left'的情况,这是一个左连接</code></pre>
<pre><code class="python">pd.merge(dframe1,dframe2,on='key',how='left')
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
<td>NaN</td>
</tr>
<tr>
<th>1</th>
<td>Z</td>
<td>1</td>
<td>3.0</td>
</tr>
<tr>
<th>2</th>
<td>Y</td>
<td>2</td>
<td>2.0</td>
</tr>
<tr>
<th>3</th>
<td>Z</td>
<td>3</td>
<td>3.0</td>
</tr>
<tr>
<th>4</th>
<td>X</td>
<td>4</td>
<td>NaN</td>
</tr>
<tr>
<th>5</th>
<td>X</td>
<td>5</td>
<td>NaN</td>
</tr>
</tbody>
</table>
<p>我们可以看到返回的是dframe1的所有key值对应的结果,如果在dframe2中不存在,显示为Nan空值</p>
<h3>1.3 RightMerge (右连接)</h3>
<p>右连接的原理和左连接正相反</p>
<pre><code class="python">pd.merge(dframe1,dframe2,on='key',how='right')
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Z</td>
<td>1.0</td>
<td>3</td>
</tr>
<tr>
<th>1</th>
<td>Z</td>
<td>3.0</td>
<td>3</td>
</tr>
<tr>
<th>2</th>
<td>Y</td>
<td>2.0</td>
<td>2</td>
</tr>
<tr>
<th>3</th>
<td>Q</td>
<td>NaN</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>这里Q只存在于drame2的key中</p>
<h3>1.4 OuterMerge (全连接)</h3>
<pre><code class="python">#Choosing the "outer" method selects the union of both keys
pd.merge(dframe1,dframe2,on='key',how='outer')
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>value_df1</th>
<th>value_df2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0.0</td>
<td>NaN</td>
</tr>
<tr>
<th>1</th>
<td>X</td>
<td>4.0</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>X</td>
<td>5.0</td>
<td>NaN</td>
</tr>
<tr>
<th>3</th>
<td>Z</td>
<td>1.0</td>
<td>3.0</td>
</tr>
<tr>
<th>4</th>
<td>Z</td>
<td>3.0</td>
<td>3.0</td>
</tr>
<tr>
<th>5</th>
<td>Y</td>
<td>2.0</td>
<td>2.0</td>
</tr>
<tr>
<th>6</th>
<td>Q</td>
<td>NaN</td>
<td>1.0</td>
</tr>
</tbody>
</table>
<pre><code class="python"> 这里就是一个并集的形式啦,其实就是一个union的结果,会把key这一列在两个Dataframe出现的所有值全部显示出来,如果有空值显示为Nan</code></pre>
<h3>1.5 MultipleKey Merge (基于多个key上的merge)</h3>
<p>刚才我们都是仅仅实现的在一个key上的merge,当然我们也可以实现基于多个keys的merge</p>
<pre><code class="python"># Dframe on left
df_left = DataFrame({'key1': ['SF', 'SF', 'LA'],
'key2': ['one', 'two', 'one'],
'left_data': [10,20,30]})
df_left</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>left_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>10</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>two</td>
<td>20</td>
</tr>
<tr>
<th>2</th>
<td>LA</td>
<td>one</td>
<td>30</td>
</tr>
</tbody>
</table>
<pre><code class="python">#Dframe on right
df_right = DataFrame({'key1': ['SF', 'SF', 'LA', 'LA'],
'key2': ['one', 'one', 'one', 'two'],
'right_data': [40,50,60,70]})
df_right</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>right_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>40</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>one</td>
<td>50</td>
</tr>
<tr>
<th>2</th>
<td>LA</td>
<td>one</td>
<td>60</td>
</tr>
<tr>
<th>3</th>
<td>LA</td>
<td>two</td>
<td>70</td>
</tr>
</tbody>
</table>
<pre><code class="python">这是内连接(交集)的结果</code></pre>
<pre><code class="python">#Merge, Inner
pd.merge(df_left, df_right, on=['key1', 'key2'])</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>left_data</th>
<th>right_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>40</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>50</td>
</tr>
<tr>
<th>2</th>
<td>LA</td>
<td>one</td>
<td>30</td>
<td>60</td>
</tr>
</tbody>
</table>
<pre><code class="python">这是外连接(并集)的结果</code></pre>
<pre><code class="python">#Merge, Outer
pd.merge(df_left, df_right, on=['key1', 'key2'],how='outer')</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>left_data</th>
<th>right_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>10.0</td>
<td>40.0</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>one</td>
<td>10.0</td>
<td>50.0</td>
</tr>
<tr>
<th>2</th>
<td>SF</td>
<td>two</td>
<td>20.0</td>
<td>NaN</td>
</tr>
<tr>
<th>3</th>
<td>LA</td>
<td>one</td>
<td>30.0</td>
<td>60.0</td>
</tr>
<tr>
<th>4</th>
<td>LA</td>
<td>two</td>
<td>NaN</td>
<td>70.0</td>
</tr>
</tbody>
</table>
<p>这里还有一个地方非常有意思,大家可以发现现在df_left,df_right作为key的两列分别是key1和key2,它们的名字是相同的,刚刚我们是通过制定on=['key1', 'key2'],那如果我们只指定一列会怎么样呢?</p>
<pre><code class="python">pd.merge(df_left,df_right,on='key1')</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2_x</th>
<th>left_data</th>
<th>key2_y</th>
<th>right_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>one</td>
<td>40</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>one</td>
<td>50</td>
</tr>
<tr>
<th>2</th>
<td>SF</td>
<td>two</td>
<td>20</td>
<td>one</td>
<td>40</td>
</tr>
<tr>
<th>3</th>
<td>SF</td>
<td>two</td>
<td>20</td>
<td>one</td>
<td>50</td>
</tr>
<tr>
<th>4</th>
<td>LA</td>
<td>one</td>
<td>30</td>
<td>one</td>
<td>60</td>
</tr>
<tr>
<th>5</th>
<td>LA</td>
<td>one</td>
<td>30</td>
<td>two</td>
<td>70</td>
</tr>
</tbody>
</table>
<p>大家可以看到pandas自动把key2这一列拆分成了key2_x和key2_y,都会显示在最后的merge结果里,如果我们想要给这两列重新命名,也是很容易的:</p>
<pre><code class="python"># We can also specify what the suffix becomes
pd.merge(df_left,df_right, on='key1',suffixes=('_lefty','_righty'))</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2_lefty</th>
<th>left_data</th>
<th>key2_righty</th>
<th>right_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>one</td>
<td>40</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>one</td>
<td>10</td>
<td>one</td>
<td>50</td>
</tr>
<tr>
<th>2</th>
<td>SF</td>
<td>two</td>
<td>20</td>
<td>one</td>
<td>40</td>
</tr>
<tr>
<th>3</th>
<td>SF</td>
<td>two</td>
<td>20</td>
<td>one</td>
<td>50</td>
</tr>
<tr>
<th>4</th>
<td>LA</td>
<td>one</td>
<td>30</td>
<td>one</td>
<td>60</td>
</tr>
<tr>
<th>5</th>
<td>LA</td>
<td>one</td>
<td>30</td>
<td>two</td>
<td>70</td>
</tr>
</tbody>
</table>
<p>像这样,我们可以通过suffixes参数来指定拆分的列的名字。</p>
<h3>1.6 Merge on Index (基于index上的merge)</h3>
<pre><code class="python">我们还可以实现几个Dataframe基于Index的merge,还是老样子,先让我们创建两个Dataframe</code></pre>
<pre><code class="python">df_left = DataFrame({'key': ['X','Y','Z','X','Y'],
'data': range(5)})
df_right = DataFrame({'group_data': [10, 20]}, index=['X', 'Y'])</code></pre>
<pre><code class="python">df_left</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
</tr>
<tr>
<th>1</th>
<td>Y</td>
<td>1</td>
</tr>
<tr>
<th>2</th>
<td>Z</td>
<td>2</td>
</tr>
<tr>
<th>3</th>
<td>X</td>
<td>3</td>
</tr>
<tr>
<th>4</th>
<td>Y</td>
<td>4</td>
</tr>
</tbody>
</table>
<pre><code class="python">df_right</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>group_data</th>
</tr></thead>
<tbody>
<tr>
<th>X</th>
<td>10</td>
</tr>
<tr>
<th>Y</th>
<td>20</td>
</tr>
</tbody>
</table>
<p>好了,现在我们想要实现两个Dataframe的merge,但是条件是通过df_left的Key和df_right的Index</p>
<pre><code class="python">pd.merge(df_left,df_right,left_on='key',right_index=True)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>data</th>
<th>group_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
<td>10</td>
</tr>
<tr>
<th>3</th>
<td>X</td>
<td>3</td>
<td>10</td>
</tr>
<tr>
<th>1</th>
<td>Y</td>
<td>1</td>
<td>20</td>
</tr>
<tr>
<th>4</th>
<td>Y</td>
<td>4</td>
<td>20</td>
</tr>
</tbody>
</table>
<p>这样我们也可以得到结果。</p>
<pre><code class="python"># We can also get a union by using outer
pd.merge(df_left,df_right,left_on='key',right_index=True,how='outer')</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>data</th>
<th>group_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
<td>10.0</td>
</tr>
<tr>
<th>3</th>
<td>X</td>
<td>3</td>
<td>10.0</td>
</tr>
<tr>
<th>1</th>
<td>Y</td>
<td>1</td>
<td>20.0</td>
</tr>
<tr>
<th>4</th>
<td>Y</td>
<td>4</td>
<td>20.0</td>
</tr>
<tr>
<th>2</th>
<td>Z</td>
<td>2</td>
<td>NaN</td>
</tr>
</tbody>
</table>
<p>其他的merge方式就类似啦,这里就不一一说了,只是举一个outer join的例子</p>
<pre><code class="python"># 通过outer实现外连接,union并集
pd.merge(df_left,df_right,left_on='key',right_index=True,how='outer')</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key</th>
<th>data</th>
<th>group_data</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>X</td>
<td>0</td>
<td>10.0</td>
</tr>
<tr>
<th>3</th>
<td>X</td>
<td>3</td>
<td>10.0</td>
</tr>
<tr>
<th>1</th>
<td>Y</td>
<td>1</td>
<td>20.0</td>
</tr>
<tr>
<th>4</th>
<td>Y</td>
<td>4</td>
<td>20.0</td>
</tr>
<tr>
<th>2</th>
<td>Z</td>
<td>2</td>
<td>NaN</td>
</tr>
</tbody>
</table>
<pre><code class="python">我们也可以尝试一些有意思的merge,比如,如果一个dataframe的index是多层嵌套的情况:</code></pre>
<pre><code class="python">df_left_hr = DataFrame({'key1': ['SF','SF','SF','LA','LA'],
'key2': [10, 20, 30, 20, 30],
'data_set': np.arange(5.)})
df_right_hr = DataFrame(np.arange(10).reshape((5, 2)),
index=[['LA','LA','SF','SF','SF'],
[20, 10, 10, 10, 20]],
columns=['col_1', 'col_2'])</code></pre>
<pre><code class="python">df_left_hr</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>data_set</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>10</td>
<td>0.0</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>20</td>
<td>1.0</td>
</tr>
<tr>
<th>2</th>
<td>SF</td>
<td>30</td>
<td>2.0</td>
</tr>
<tr>
<th>3</th>
<td>LA</td>
<td>20</td>
<td>3.0</td>
</tr>
<tr>
<th>4</th>
<td>LA</td>
<td>30</td>
<td>4.0</td>
</tr>
</tbody>
</table>
<pre><code class="python">df_right_hr</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th></th>
<th>col_1</th>
<th>col_2</th>
</tr></thead>
<tbody>
<tr>
<th rowspan="2">LA</th>
<th>20</th>
<td>0</td>
<td>1</td>
</tr>
<tr>
<th>10</th>
<td>2</td>
<td>3</td>
</tr>
<tr>
<th rowspan="3">SF</th>
<th>10</th>
<td>4</td>
<td>5</td>
</tr>
<tr>
<th>10</th>
<td>6</td>
<td>7</td>
</tr>
<tr>
<th>20</th>
<td>8</td>
<td>9</td>
</tr>
</tbody>
</table>
<p>现在我们穿建了两个Dataframe 分别是df_left_hr和df_right_hr(Index两层),如果我们想通过使用df_left_hr的key1,key2 及df_right_hr的Index作为merge<br>的列,也是没有问题的</p>
<pre><code class="python"># Now we can merge the left by using keys and the right by its index
pd.merge(df_left_hr,df_right_hr,left_on=['key1','key2'],right_index=True)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>key1</th>
<th>key2</th>
<th>data_set</th>
<th>col_1</th>
<th>col_2</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>SF</td>
<td>10</td>
<td>0.0</td>
<td>4</td>
<td>5</td>
</tr>
<tr>
<th>0</th>
<td>SF</td>
<td>10</td>
<td>0.0</td>
<td>6</td>
<td>7</td>
</tr>
<tr>
<th>1</th>
<td>SF</td>
<td>20</td>
<td>1.0</td>
<td>8</td>
<td>9</td>
</tr>
<tr>
<th>3</th>
<td>LA</td>
<td>20</td>
<td>3.0</td>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>基本到这里,我已经和大家分享了基础的Merge有关的所有操作,如果你平时生活工作中经常使用Excel执行类似操作的话,可以学习一下Merge哈,它会大幅度<br>减轻你的工作强度的!</p>
<h2>2.Join</h2>
<p>现在我们可以接着来看join相关的操作,先让我们看一个小例子</p>
<pre><code class="python">left = pd.DataFrame({'A': ['A0', 'A1', 'A2', 'A3'],
'B': ['B0', 'B1', 'B2', 'B3']},
index = ['K0', 'K1', 'K2', 'K3'])
right = pd.DataFrame({'C': ['C0', 'C1', 'C2', 'C3'],
'D': ['D0', 'D1', 'D2', 'D3']},
index = ['K0', 'K1', 'K2', 'K3']) </code></pre>
<pre><code class="python">left</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>A</th>
<th>B</th>
</tr></thead>
<tbody>
<tr>
<th>K0</th>
<td>A0</td>
<td>B0</td>
</tr>
<tr>
<th>K1</th>
<td>A1</td>
<td>B1</td>
</tr>
<tr>
<th>K2</th>
<td>A2</td>
<td>B2</td>
</tr>
<tr>
<th>K3</th>
<td>A3</td>
<td>B3</td>
</tr>
</tbody>
</table>
<pre><code class="python">right</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>C</th>
<th>D</th>
</tr></thead>
<tbody>
<tr>
<th>K0</th>
<td>C0</td>
<td>D0</td>
</tr>
<tr>
<th>K1</th>
<td>C1</td>
<td>D1</td>
</tr>
<tr>
<th>K2</th>
<td>C2</td>
<td>D2</td>
</tr>
<tr>
<th>K3</th>
<td>C3</td>
<td>D3</td>
</tr>
</tbody>
</table>
<pre><code class="python">left.join(right)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>A</th>
<th>B</th>
<th>C</th>
<th>D</th>
</tr></thead>
<tbody>
<tr>
<th>K0</th>
<td>A0</td>
<td>B0</td>
<td>C0</td>
<td>D0</td>
</tr>
<tr>
<th>K1</th>
<td>A1</td>
<td>B1</td>
<td>C1</td>
<td>D1</td>
</tr>
<tr>
<th>K2</th>
<td>A2</td>
<td>B2</td>
<td>C2</td>
<td>D2</td>
</tr>
<tr>
<th>K3</th>
<td>A3</td>
<td>B3</td>
<td>C3</td>
<td>D3</td>
</tr>
</tbody>
</table>
<p>其实通过这一个小例子大家也就明白了,join无非就是合并,默认是横向,还有一个点需要注意的是,我们其实可以通过join实现和merge一样的效果,但是为了<br>避免混淆,我不会多举其他的例子了,因为我个人认为一般情况下还是用merge函数好一些</p>
<h2>3. Concat</h2>
<p>为了更加全面彻底地了解Concat函数,大家可以先从一维的Numpy Array开始,首先让我们简单的创建一个矩阵:</p>
<pre><code class="python"># Create a matrix
arr1 = np.arange(9).reshape((3,3))
arr1</code></pre>
<pre><code>array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
</code></pre>
<p>接着让我们通过concatenate函数进行横向拼接:</p>
<pre><code class="python">np.concatenate([arr1,arr1],axis=1)</code></pre>
<pre><code>array([[0, 1, 2, 0, 1, 2],
[3, 4, 5, 3, 4, 5],
[6, 7, 8, 6, 7, 8]])
</code></pre>
<p>再让我们进行纵向拼接:</p>
<pre><code class="python"># Let's see other axis options
np.concatenate([arr1,arr1],axis=0)</code></pre>
<pre><code>array([[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 1, 2],
[3, 4, 5],
[6, 7, 8]])
</code></pre>
<p>有了基础的印象之后,现在让我们看看在pandas中是如何操作的:</p>
<pre><code class="python"># Lets create two Series with no overlap
ser1 = Series([0,1,2],index=['T','U','V'])
ser2 = Series([3,4],index=['X','Y'])
#Now let use concat (default is axis=0)
pd.concat([ser1,ser2])</code></pre>
<pre><code>T 0
U 1
V 2
X 3
Y 4
dtype: int64
</code></pre>
<p>在上面的例子中,我们分别创建了两个没有重复Index的Series,然后用concat默认的把它们合并在一起,这时生成的依然是Series类型,如果我们把axis换成1,那生成的就是Dataframe,像下面一样</p>
<pre><code class="python">pd.concat([ser1,ser2],axis=1,sort =True) # sort=Ture是默认的,pandas总是默认index排序</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>0</th>
<th>1</th>
</tr></thead>
<tbody>
<tr>
<th>T</th>
<td>0.0</td>
<td>NaN</td>
</tr>
<tr>
<th>U</th>
<td>1.0</td>
<td>NaN</td>
</tr>
<tr>
<th>V</th>
<td>2.0</td>
<td>NaN</td>
</tr>
<tr>
<th>X</th>
<td>NaN</td>
<td>3.0</td>
</tr>
<tr>
<th>Y</th>
<td>NaN</td>
<td>4.0</td>
</tr>
</tbody>
</table>
<p>我们还可以指定在哪些index上进行concat:</p>
<pre><code class="python">pd.concat([ser1,ser2],axis=1,join_axes=[['U','V','Y']])</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>0</th>
<th>1</th>
</tr></thead>
<tbody>
<tr>
<th>U</th>
<td>1.0</td>
<td>NaN</td>
</tr>
<tr>
<th>V</th>
<td>2.0</td>
<td>NaN</td>
</tr>
<tr>
<th>Y</th>
<td>NaN</td>
<td>4.0</td>
</tr>
</tbody>
</table>
<p>也可以给不同组的index加一层标签</p>
<pre><code class="python">pd.concat([ser1,ser2],keys=['cat1','cat2'])</code></pre>
<pre><code>cat1 T 0
U 1
V 2
cat2 X 3
Y 4
dtype: int64
</code></pre>
<p>如果把axis换成是1,那么keys就会变成column的名字:</p>
<pre><code class="python">pd.concat([ser1,ser2],axis=1,keys=['cat1','cat2'],sort=True)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>cat1</th>
<th>cat2</th>
</tr></thead>
<tbody>
<tr>
<th>T</th>
<td>0.0</td>
<td>NaN</td>
</tr>
<tr>
<th>U</th>
<td>1.0</td>
<td>NaN</td>
</tr>
<tr>
<th>V</th>
<td>2.0</td>
<td>NaN</td>
</tr>
<tr>
<th>X</th>
<td>NaN</td>
<td>3.0</td>
</tr>
<tr>
<th>Y</th>
<td>NaN</td>
<td>4.0</td>
</tr>
</tbody>
</table>
<p>如果是两个现成的dataframe直接进行concat也是一样:</p>
<pre><code class="python">dframe1 = DataFrame(np.random.randn(4,3), columns=['X', 'Y', 'Z'])
dframe2 = DataFrame(np.random.randn(3, 3), columns=['Y', 'Q', 'X'])</code></pre>
<pre><code class="python">dframe1</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>X</th>
<th>Y</th>
<th>Z</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>1.119976</td>
<td>-0.853960</td>
<td>0.027451</td>
</tr>
<tr>
<th>1</th>
<td>-0.536831</td>
<td>0.982092</td>
<td>-0.157650</td>
</tr>
<tr>
<th>2</th>
<td>-0.219322</td>
<td>-1.489809</td>
<td>1.607735</td>
</tr>
<tr>
<th>3</th>
<td>0.767249</td>
<td>-1.661912</td>
<td>0.038837</td>
</tr>
</tbody>
</table>
<pre><code class="python">dframe2</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Y</th>
<th>Q</th>
<th>X</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>-0.035560</td>
<td>0.875282</td>
<td>-1.630508</td>
</tr>
<tr>
<th>1</th>
<td>-0.439484</td>
<td>0.096247</td>
<td>1.335693</td>
</tr>
<tr>
<th>2</th>
<td>0.746299</td>
<td>0.568684</td>
<td>1.197015</td>
</tr>
</tbody>
</table>
<pre><code class="python">#如果没有对应的值,默认为NaN, 空值
pd.concat([dframe1,dframe2],sort=True)</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Q</th>
<th>X</th>
<th>Y</th>
<th>Z</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>NaN</td>
<td>1.119976</td>
<td>-0.853960</td>
<td>0.027451</td>
</tr>
<tr>
<th>1</th>
<td>NaN</td>
<td>-0.536831</td>
<td>0.982092</td>
<td>-0.157650</td>
</tr>
<tr>
<th>2</th>
<td>NaN</td>
<td>-0.219322</td>
<td>-1.489809</td>
<td>1.607735</td>
</tr>
<tr>
<th>3</th>
<td>NaN</td>
<td>0.767249</td>
<td>-1.661912</td>
<td>0.038837</td>
</tr>
<tr>
<th>0</th>
<td>0.875282</td>
<td>-1.630508</td>
<td>-0.035560</td>
<td>NaN</td>
</tr>
<tr>
<th>1</th>
<td>0.096247</td>
<td>1.335693</td>
<td>-0.439484</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>0.568684</td>
<td>1.197015</td>
<td>0.746299</td>
<td>NaN</td>
</tr>
</tbody>
</table>
<h2>4. 源码及Github地址</h2>
<p>今天我为大家主要总结了pandas中非常常见的三种方法:</p>
<ul>
<li>merge</li>
<li>concat</li>
<li>join</li>
</ul>
<p>大家可以根据自己的实际需要来决定使用哪一种</p>
<p>我把这一期的ipynb文件和py文件放到了Github上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=ZunK%2BKlHSm0e5iyeXL3pMw%3D%3D.USPvVe4s6KRT%2Bd8AzQ2SzmWRb01wCtYnQiggS6PNJj735OlkMDwtEeQv9yOCdNvz" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>这一期就到这里啦,希望大家能够继续支持我,完结,撒花</p>
Pandas之旅(二): 有关数据清理的点点滴滴
https://segmentfault.com/a/1190000018441469
2019-03-09T07:04:03+08:00
2019-03-09T07:04:03+08:00
alpha94511
https://segmentfault.com/u/alpha94511
5
<h2>数据清洗</h2>
<p>大家好,这一期我将为大家带来我的pandas学习心得第二期:数据清理。这一步非常重要,一般在获取数据源之后,我们紧接着就要开始这一步,以便为了之后的各种操作,简单来说,我们的目标就是让数据看起来赏心悦目,规规矩矩的,所以我们会对原始的dataframe做一些必要的美容,包括规范命名,去除异常值,重新选择合适的index啊,处理缺失值,统一列的命名等等。</p>
<p>这一期我会和大家分享一些比较好用常见的清洗方法。首先还是让我们来简单看一下本文将会用到的数据源:</p>
<ul><li>
<strong>property_data.csv</strong> 这是一个超小型的房地产行业的数据集,大家会在文章最后找到下载地址。</li></ul>
<p>这篇文章我会从以下几个方面来和大家分享我的心得体会:</p>
<ol>
<li>有关缺失值的处理</li>
<li>有关列的处理</li>
<li>设置Index</li>
<li>源码及数据下载地址</li>
</ol>
<h2>1.有关缺失值的处理</h2>
<p>这里我们会用到 property_data.csv这个数据集,在开始处理缺失值之前,我们可以先话一分钟仔细想想,为什么实际生活中的数据从来是不完整的,原因基本有几个方面:</p>
<ul>
<li>用户忘记填写字段</li>
<li>从旧数据库手动传输时数据丢失</li>
<li>代码中有bug</li>
<li>用户不填写非必须字段(比如注册的时候)</li>
</ul>
<p>因为这些原因,我每次在处理missing value的时候都会问自己两个基础问题:</p>
<ol>
<li>数据集每一列有什么特点?</li>
<li>我们想要在处理后得到什么类型的数据(int,float,string,boolean)?</li>
</ol>
<p>带着这些疑问,我们可以开始了,首先让我们简单读取一下数据,利用head函数看看前5行,如果你还对pandas的基础知识有疑问,可以看看我上一篇文章:<a href="https://segmentfault.com/a/1190000018373808">Pandas之旅(一): 让我们把基础知识一次撸完,申精干货</a></p>
<pre><code class="python">import pandas as pd
import numpy as np
import os
os.chdir("F:\\Python教程\\segmentfault\\pandas_share\\Pandas之旅_02 数据清洗")</code></pre>
<pre><code class="python"># Read csv file into a pandas dataframe
df = pd.read_csv("property_data.csv")
# Take a look at the first few rows
df.head()</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>PID</th>
<th>ST_NUM</th>
<th>ST_NAME</th>
<th>OWN_OCCUPIED</th>
<th>NUM_BEDROOMS</th>
<th>NUM_BATH</th>
<th>SQ_FT</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3</td>
<td>1</td>
<td>1000</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3</td>
<td>1.5</td>
<td>--</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>NaN</td>
<td>LEXINGTON</td>
<td>N</td>
<td>NaN</td>
<td>1</td>
<td>850</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>12</td>
<td>1</td>
<td>NaN</td>
<td>700</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3</td>
<td>2</td>
<td>1600</td>
</tr>
</tbody>
</table>
<p>现在让我们看看数据的一些关键列是什么:</p>
<ul>
<li>ST_NUM:街道号码</li>
<li>ST_NAME: 街道名称</li>
<li>OWN_OCCUPIED: 是否用于自住</li>
<li>NUM_BEDROOMS:卧室数量</li>
<li>SQ_FT:面积</li>
</ul>
<p>这里可以给大家普及点房地产知识,有的时候房屋用途被明确规定,比如有的房产写的是"owner occupied only ")意思是说如果你买了,那这个房子会成为你的主要住所,不能用于出租之类的,简单理解就是自住</p>
<p>所以现在我可以自问自答第一个问题:数据集每一列有什么特点?</p>
<ul>
<li>ST_NUM:float或int ...</li>
<li>ST_NAME:string</li>
<li>OWN_OCCUPIED:string ... Y(“是”)或N(“否”)</li>
<li>NUM_BEDROOMS:float或int,数字类型</li>
<li>SQ_FT:float或int,数字类型</li>
</ul>
<h3>1.1 规范的缺失值标记</h3>
<p>现在让我们关注ST_NUM这一列:</p>
<pre><code class="python"># Looking at the ST_NUM column
df['ST_NUM']</code></pre>
<pre><code>0 104.0
1 197.0
2 NaN
3 201.0
4 203.0
5 207.0
6 NaN
7 213.0
8 215.0
Name: ST_NUM, dtype: float64
</code></pre>
<p>如果想查看该列的缺失值情况,我们可以利用isnull()方法,如果出现缺失值,会返回True,反之返回false</p>
<pre><code class="python">df['ST_NUM'].isnull()</code></pre>
<pre><code>0 False
1 False
2 True
3 False
4 False
5 False
6 True
7 False
8 False
Name: ST_NUM, dtype: bool
</code></pre>
<p>但是其实如果我们打开csv文件,你会发现第3行是空白,还有一行在该列显示的是NA,所以结论已经有了:在pandas里表示缺省值的符号及时NA,换句话说,如果我们要表示缺省值,标准写法是NA</p>
<p><img src="/img/bVbpxC1?w=631&h=241" alt="clipboard.png" title="clipboard.png"></p>
<h3>1.2 不规范的缺失值标记</h3>
<p>同样的,这回让我们关注一下NUM_BEDROOMS这一列,我们发现出现了4种类型的表达缺省值的标记:</p>
<ul>
<li>n/a</li>
<li>NA</li>
<li>—</li>
<li>na</li>
</ul>
<p><img src="/img/bVbpxC2?w=870&h=478" alt="clipboard.png" title="clipboard.png"></p>
<p>通过刚才的实践,我们已经确定NA是pandas可以识别的,那么其他的符号呢,现在让我们来测试一下</p>
<pre><code class="python">df['NUM_BEDROOMS']</code></pre>
<pre><code>0 3
1 3
2 NaN
3 1
4 3
5 NaN
6 2
7 1
8 na
Name: NUM_BEDROOMS, dtype: object
</code></pre>
<pre><code class="python">df['NUM_BEDROOMS'].isnull()</code></pre>
<pre><code>0 False
1 False
2 True
3 False
4 False
5 True
6 False
7 False
8 False
Name: NUM_BEDROOMS, dtype: bool
</code></pre>
<p>可以看到pandas识别了n/a 和NA两种符号,但是接下来我们要考虑一个问题,假设你是房地产公司的地区总经理,你每周会收到不同地区的负责人提交的表格,<br>这些人中有的喜欢用--表示空白值,有的人喜欢用na,那应该怎么办?</p>
<p>最简单的方式就是将所有表示空白值的符号统一放在list中,让后让pandas一次性识别:</p>
<pre><code class="python"># Making a list of missing value types
missing_values = ["na", "--"]
df = pd.read_csv("property_data.csv", na_values = missing_values)</code></pre>
<p>现在我们来看看到底发生了什么?</p>
<pre><code class="python">df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>PID</th>
<th>ST_NUM</th>
<th>ST_NAME</th>
<th>OWN_OCCUPIED</th>
<th>NUM_BEDROOMS</th>
<th>NUM_BATH</th>
<th>SQ_FT</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>NaN</td>
<td>LEXINGTON</td>
<td>N</td>
<td>NaN</td>
<td>1</td>
<td>850.0</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>12</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
</tr>
<tr>
<th>5</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>NaN</td>
<td>1</td>
<td>800.0</td>
</tr>
<tr>
<th>6</th>
<td>100007000.0</td>
<td>NaN</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
</tr>
<tr>
<th>7</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
</tr>
<tr>
<th>8</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>NaN</td>
<td>2</td>
<td>1800.0</td>
</tr>
</tbody>
</table>
<p>我们可以发现只要missing_value中记录的表达空白值的符号,全部变成了规整的NaN</p>
<h3>1.3 类型不一致的异常值</h3>
<p>刚刚我们已经简单了解了在pandas中如何处理缺失值的,还有一种情况,让我们来看OWN_OCCUPIED这一列,这一列的答案只能是Y,N 但是我们发现数据集意外地出现了12,属于类型不对称</p>
<p><img src="/img/bVbpxC4?w=874&h=482" alt="clipboard.png" title="clipboard.png"></p>
<pre><code class="python">df['OWN_OCCUPIED'].isnull()</code></pre>
<pre><code>0 False
1 False
2 False
3 False
4 False
5 False
6 True
7 False
8 False
Name: OWN_OCCUPIED, dtype: bool
</code></pre>
<p>现在我们发现12是异常值,因为它是类型错误,所以我们可以简单通过下面这个方法来检测,</p>
<pre><code class="python"># Detecting numbers
cnt=0
for row in df['OWN_OCCUPIED']:
try:
int(row)
df.loc[cnt, 'OWN_OCCUPIED']=np.nan
except ValueError:
pass
cnt+=1</code></pre>
<p>我们这里的策略是:</p>
<ul>
<li>循环遍历OWN_OCCUPIED列</li>
<li>尝试将条目转换为整数</li>
<li>如果条目可以更改为整数,请输入缺失值</li>
<li>如果数字不能是整数,我们知道它是一个字符串,所以继续</li>
</ul>
<p>这样我们会把OWN_OCCUPIED这一列中所有类型不对的值转化为NaN,现在来看结果:</p>
<pre><code class="python">df['OWN_OCCUPIED']</code></pre>
<pre><code>0 Y
1 N
2 N
3 NaN
4 Y
5 Y
6 NaN
7 Y
8 Y
Name: OWN_OCCUPIED, dtype: object
</code></pre>
<h3>1.4 汇总缺失值</h3>
<p>pandas提供了更为简洁的方式,可以让我们整体了解所有column的空值:</p>
<pre><code class="python">df.isnull().sum()</code></pre>
<pre><code>PID 1
ST_NUM 2
ST_NAME 0
OWN_OCCUPIED 2
NUM_BEDROOMS 3
NUM_BATH 1
SQ_FT 2
dtype: int64
</code></pre>
<p>或者如果我们只想知道数据是否存在空值,那么可以使用以下的命令:</p>
<pre><code class="python"># Any missing values?
df.isnull().values.any()</code></pre>
<pre><code>True
</code></pre>
<h3>1.5 替换缺失值</h3>
<p>如果我们想要替换掉缺失值,可以用fillna方法</p>
<pre><code class="python"># Replace missing values with a number
df['ST_NUM'].fillna(125, inplace=True)</code></pre>
<p>或者我们可以通过准确定位来替换缺失值:</p>
<pre><code class="python"># Location based replacement
df.loc[2,'ST_NUM'] = 125</code></pre>
<p>替换缺失值的一种非常常见的方法是使用中位数:</p>
<pre><code class="python"># Replace using median
median = df['NUM_BEDROOMS'].median()
df['NUM_BEDROOMS'].fillna(median, inplace=True)
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>PID</th>
<th>ST_NUM</th>
<th>ST_NAME</th>
<th>OWN_OCCUPIED</th>
<th>NUM_BEDROOMS</th>
<th>NUM_BATH</th>
<th>SQ_FT</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
</tr>
<tr>
<th>5</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>2.5</td>
<td>1</td>
<td>800.0</td>
</tr>
<tr>
<th>6</th>
<td>100007000.0</td>
<td>125.0</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
</tr>
<tr>
<th>7</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
</tr>
<tr>
<th>8</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>2.5</td>
<td>2</td>
<td>1800.0</td>
</tr>
</tbody>
</table>
<h2>2. 有关列的处理</h2>
<h3>2.1 统一修改列名</h3>
<p>现在假设因为一些需求,需要我们统一修改列名,把列名改为小写,我们可以结合列表推导式轻易实现</p>
<pre><code class="python">df.rename(str.lower, axis='columns',inplace =True)
df.columns</code></pre>
<pre><code>Index(['pid', 'st_num', 'st_name', 'own_occupied', 'num_bedrooms', 'num_bath',
'sq_ft'],
dtype='object')
</code></pre>
<p>或者需要把列名中的_改为-:</p>
<pre><code class="python">new_cols = [c.replace("_","-") for c in df.columns]
change_dict =dict(zip(df.columns,new_cols))
df.rename(columns=change_dict,inplace=True)
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
<th>own-occupied</th>
<th>num-bedrooms</th>
<th>num-bath</th>
<th>sq-ft</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
</tr>
<tr>
<th>5</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>2.5</td>
<td>1</td>
<td>800.0</td>
</tr>
<tr>
<th>6</th>
<td>100007000.0</td>
<td>125.0</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
</tr>
<tr>
<th>7</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
</tr>
<tr>
<th>8</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>2.5</td>
<td>2</td>
<td>1800.0</td>
</tr>
</tbody>
</table>
<p>这里我没有写的精简一些,反而是复杂了,主要是想让大家回忆起之前我分享的dict使用技巧中的内容,注意这里inplace=True,导致的结果是我们的的确确修改了df所有的列名</p>
<h3>2.1 根据需求新增列</h3>
<p>假如目前我们需要新增一列,根据房屋面积大小来赋值,我们先随意把缺失值补上:</p>
<pre><code class="python">df['sq-ft'].fillna('0.0')</code></pre>
<pre><code>0 1000
1 0.0
2 850
3 700
4 1600
5 800
6 950
7 0.0
8 1800
Name: sq-ft, dtype: object
</code></pre>
<p>然后新建一列rank来根据房屋面积大小赋值S=small,M=medium,B=big:</p>
<pre><code class="python">df["rank"]= pd.cut(df['sq-ft'], [0, 800, 1600, np.inf], labels=("S","M","B"))
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
<th>own-occupied</th>
<th>num-bedrooms</th>
<th>num-bath</th>
<th>sq-ft</th>
<th>rank</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
<td>M</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
<td>M</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
<td>S</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
<td>M</td>
</tr>
<tr>
<th>5</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>2.5</td>
<td>1</td>
<td>800.0</td>
<td>S</td>
</tr>
<tr>
<th>6</th>
<td>100007000.0</td>
<td>125.0</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
<td>M</td>
</tr>
<tr>
<th>7</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>8</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>2.5</td>
<td>2</td>
<td>1800.0</td>
<td>B</td>
</tr>
</tbody>
</table>
<p>具体实现方法我们之后会说,这里主要是用到了pandas的cut方法,非常便捷</p>
<h2>3. 设置Index</h2>
<p>在许多情况下,使用数据的唯一值标识字段作为其索引是有帮助的。这里可能我们的数据不太合适,因此我们先伪造一列Fake_Index来模拟真实场景中的真正索引</p>
<pre><code class="python">df["Fake_Index"]=["A00"+str(i) for i in range(len(df))]
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
<th>own-occupied</th>
<th>num-bedrooms</th>
<th>num-bath</th>
<th>sq-ft</th>
<th>rank</th>
<th>Fake_Index</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
<td>M</td>
<td>A000</td>
</tr>
<tr>
<th>1</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
<td>NaN</td>
<td>A001</td>
</tr>
<tr>
<th>2</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
<td>M</td>
<td>A002</td>
</tr>
<tr>
<th>3</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
<td>S</td>
<td>A003</td>
</tr>
<tr>
<th>4</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
<td>M</td>
<td>A004</td>
</tr>
<tr>
<th>5</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>2.5</td>
<td>1</td>
<td>800.0</td>
<td>S</td>
<td>A005</td>
</tr>
<tr>
<th>6</th>
<td>100007000.0</td>
<td>125.0</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
<td>M</td>
<td>A006</td>
</tr>
<tr>
<th>7</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
<td>NaN</td>
<td>A007</td>
</tr>
<tr>
<th>8</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>2.5</td>
<td>2</td>
<td>1800.0</td>
<td>B</td>
<td>A008</td>
</tr>
</tbody>
</table>
<p>现在我们添加的最后一列非常像真正的房屋Id了,让我们来看看这个伪造的索引是不是唯一值,可以利用is_unique来检验:</p>
<pre><code class="python">df.Fake_Index.is_unique</code></pre>
<pre><code>True
</code></pre>
<p>没有问题,现在我们可以放心地把这列设置为我们真正的索引:</p>
<pre><code class="python">df = df.set_index('Fake_Index')
df</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
<th>own-occupied</th>
<th>num-bedrooms</th>
<th>num-bath</th>
<th>sq-ft</th>
<th>rank</th>
</tr>
<tr>
<th>Fake_Index</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A000</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
<td>M</td>
</tr>
<tr>
<th>A001</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A002</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
<td>M</td>
</tr>
<tr>
<th>A003</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
<td>S</td>
</tr>
<tr>
<th>A004</th>
<td>NaN</td>
<td>203.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>3.0</td>
<td>2</td>
<td>1600.0</td>
<td>M</td>
</tr>
<tr>
<th>A005</th>
<td>100006000.0</td>
<td>207.0</td>
<td>BERKELEY</td>
<td>Y</td>
<td>2.5</td>
<td>1</td>
<td>800.0</td>
<td>S</td>
</tr>
<tr>
<th>A006</th>
<td>100007000.0</td>
<td>125.0</td>
<td>WASHINGTON</td>
<td>NaN</td>
<td>2.0</td>
<td>HURLEY</td>
<td>950.0</td>
<td>M</td>
</tr>
<tr>
<th>A007</th>
<td>100008000.0</td>
<td>213.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>1.0</td>
<td>1</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A008</th>
<td>100009000.0</td>
<td>215.0</td>
<td>TREMONT</td>
<td>Y</td>
<td>2.5</td>
<td>2</td>
<td>1800.0</td>
<td>B</td>
</tr>
</tbody>
</table>
<p>现在对数据的操作容易多了,我们很多事情可以通过索引完成:</p>
<pre><code class="python"># 根据索引名称切片
df['A000':'A003']</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
<th>own-occupied</th>
<th>num-bedrooms</th>
<th>num-bath</th>
<th>sq-ft</th>
<th>rank</th>
</tr>
<tr>
<th>Fake_Index</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A000</th>
<td>100001000.0</td>
<td>104.0</td>
<td>PUTNAM</td>
<td>Y</td>
<td>3.0</td>
<td>1</td>
<td>1000.0</td>
<td>M</td>
</tr>
<tr>
<th>A001</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>3.0</td>
<td>1.5</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>A002</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
<td>N</td>
<td>2.5</td>
<td>1</td>
<td>850.0</td>
<td>M</td>
</tr>
<tr>
<th>A003</th>
<td>100004000.0</td>
<td>201.0</td>
<td>BERKELEY</td>
<td>NaN</td>
<td>1.0</td>
<td>NaN</td>
<td>700.0</td>
<td>S</td>
</tr>
</tbody>
</table>
<pre><code class="python"># 根据索引位置切片
df.iloc[1:3, 0:3]</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>pid</th>
<th>st-num</th>
<th>st-name</th>
</tr>
<tr>
<th>Fake_Index</th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>A001</th>
<td>100002000.0</td>
<td>197.0</td>
<td>LEXINGTON</td>
</tr>
<tr>
<th>A002</th>
<td>100003000.0</td>
<td>125.0</td>
<td>LEXINGTON</td>
</tr>
</tbody>
</table>
<pre><code class="python"># 定位到具体元素
df.iloc[1,2]</code></pre>
<pre><code>'LEXINGTON'
</code></pre>
<h2>总结</h2>
<p>我把这一期的ipynb文件和py文件放到了GIthub上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=xfWNxYZtYyu1TmIwJcdjLA%3D%3D.QZw0LNY0VGa5hFAav28lpSc0agdhyAb69L2GPq125LVBjQhVoJKjbWmBmuDXDzH8" rel="nofollow">https://github.com/yaozeliang/pandas_share</a>
</li></ul>
<p>这一期先讲到这里,希望大家能够继续支持我,完结,撒花</p>
Pandas之旅(一): 让我们把基础知识一次撸完,申精干货
https://segmentfault.com/a/1190000018373808
2019-03-03T23:34:06+08:00
2019-03-03T23:34:06+08:00
alpha94511
https://segmentfault.com/u/alpha94511
14
<h2>为什么你需要pandas</h2>
<p>大家好,今天想和大家分享一下有关pandas的学习新的,我因工作需要,从去年12月开始接触这个非常好用的包,到现在为止也是算是熟悉了一些,因此发现了它的强大之处,特意想要和朋友们分享,特别是如果你每天和excel打交道,总是需要编写一些vba函数或者对行列进行groupby啊,merge,join啊之类的,相信我,pandas会让你解脱的。</p>
<p>好啦,闲话少说,这篇文章的基础是假设你已经大概听说过了pandas的一些概念和用途,如果还大体上不太清楚的朋友们,可以去百度一下相关基础介绍,本分主要分成四个部分:</p>
<ol>
<li>从Dataframe说起 : 简单了解Df这种数据结构,了解如何创建一个Df</li>
<li>Dataframe基础操作 :基于行,列,index选取数据,新增数据,删除数据</li>
<li>Pandas 读取 / 导出数据: 了解如何对excel,csv,tsv等常见文件进行读取并导出</li>
<li>总结: <strong>精华部分</strong>,为大家总结一些非常实用的一些方法,并附带源码 ipynb以及py文件提供下载</li>
</ol>
<h2>1. 从Dataframe说起</h2>
<p>首先,让我们明确一点,pandas这个包是在numpy的基础上得到的,因此可能有的朋友会有疑问,要不要先学学numpy,我的个人建议是真的不太需要,因为大多数的情况下基本用不到,当然,如果你处理的是科学实验类型的数据的话,当我没说,我这里的应用场景主要是一般类型的常见数据,比如:</p>
<ul>
<li>你是销售经理,经常需要汇总各地区的销售情况</li>
<li>你在银行工作,不可避免的需要和数字,excel打交道</li>
<li>你每天有很多重复的工作,比如备份,计算两表合并,分组,聚类等等。。。。。。</li>
</ul>
<p>这种情况下,学习pandas会非常有用,这里我们说的数据都是二维的table,在pandas中也称作dataframe。<br>pandas中其实一共有三种类型的常见数据:</p>
<table>
<thead><tr>
<th>数据结构</th>
<th>维度</th>
<th>说明</th>
</tr></thead>
<tbody>
<tr>
<td>Series</td>
<td>1</td>
<td>类似list,一维数组</td>
</tr>
<tr>
<td>Data Frames</td>
<td>2</td>
<td>最常见的二维数据结构,excel,sql的表就是这个</td>
</tr>
<tr>
<td>Panel</td>
<td>3</td>
<td>用的很少,三维结构</td>
</tr>
</tbody>
</table>
<p>pandas主要包括三类数据结构,分别是:</p>
<p>Series:一维数组,与Numpy中的一维array类似。二者与Python基本的数据结构List也很相近,其区别是:List中的元素可以是不同的数据类型,而Array和Series中则只允许存储相同的数据类型,这样可以更有效的使用内存,提高运算效率。</p>
<p>DataFrame:二维的表格型数据结构。很多功能与R中的data.frame类似。可以将DataFrame理解为Series的容器。以下的内容主要以DataFrame为主。</p>
<p>Panel :三维的数组,可以理解为DataFrame的容器。</p>
<p>Pandas官网,更多功能请参考 <a href="https://link.segmentfault.com/?enc=%2Fv6IhL9F8PiBKo%2Foj6LBlg%3D%3D.NrzGzVqoLtdlXCvZjw%2BbPa14ZM0YsEW9NiTQJLag1%2FObZy5RusRxPnko0YbWFaSG6klBSwZQypM1M2Dx%2FI36Dw%3D%3D" rel="nofollow">http://pandas-docs.github.io/...</a></p>
<p>这里我们主要聚焦在Dataframe上,一个dataframe长的基本上如下这番模样:</p>
<table>
<thead><tr>
<th>Name</th>
<th>Age</th>
<th>Mark</th>
</tr></thead>
<tbody>
<tr>
<td>John</td>
<td>15</td>
<td>78</td>
</tr>
<tr>
<td>Mike</td>
<td>23</td>
<td>86</td>
</tr>
<tr>
<td>Mary</td>
<td>36</td>
<td>95</td>
</tr>
</tbody>
</table>
<p>我们把这个简单的dataframe起个名字叫df,那么它包括的最基础的元素有:</p>
<ul>
<li>index:每行的索引,默认是从0开始,比如上面的df[0] 就是指 John 15 78这一行,依次类推,当然我们可以自己规定index,我以后会说</li>
<li>columns:列。这里就是指的是Name Age Mark 了</li>
<li>rows:行,df一共有3行</li>
</ul>
<p>现在让我们看一个完整的图示:</p>
<p><img src="/img/bVbo41v?w=1600&h=666" alt="clipboard.png" title="clipboard.png"></p>
<p>Dataframe 是应用最多也是最广泛的数据结构,现在就让我们开始学习创建一个dataframe吧~</p>
<h3>1.1 创建Dataframe</h3>
<p>创建一个Dataframe的方法有很多,总体上来说最常见的有及种:</p>
<ul>
<li>创建空dataframe</li>
<li>利用dict创建</li>
<li>从其他数据源读取(网站,excel,mysql等)</li>
</ul>
<p>真实场景中大多数都是从其他数据源读取,我们会在第3部分讲到</p>
<blockquote>创建空dataframe</blockquote>
<p>这个非常简单:</p>
<pre><code class="python">import pandas as pd
# Calling DataFrame constructor
df = pd.DataFrame()
print(df)
Out:Empty DataFrame
Columns: []
Index: []</code></pre>
<blockquote>利用dict创建(1)</blockquote>
<pre><code>import pandas as pd
print (f" Using {pd.__name__},Version {pd.__version__}")
Out: Using pandas , version 0.23.4</code></pre>
<p>首先要import pandas 包,这里缩写为pd,现在让我们从dict创建一个dataframe:</p>
<pre><code>>>> dict = {'name':["Tom", "Bob", "Mary", "James"],
'age': [18, 30, 25, 40],
'city':["Beijing", "ShangHai","GuangZhou", "ShenZhen"]}
>>> df = pd.DataFrame(dict)
>>> df
</code></pre>
<p>创建结果如下:</p>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
</tr>
</tbody>
</table>
<p>很简单是不是,大家可能发现了,最左边的东西就是index ,0,1,2,3,如果我们不指定会自动生成从0默认开始的序列,一直到你的dataframe的最后一行,类似于excel左边的编号</p>
<blockquote>利用dict创建(2)</blockquote>
<p>这次让我们创建的更加规范一些:</p>
<pre><code class="python">
index = pd.Index(["Tom", "Bob", "Mary", "James"],name = 'person')
cols = ['age','city']
data = [[18,'Beijing'],
[30,'ShangHai'],
[25,'GuangZhou'],
[40,'ShenZhen']]
df =pd.DataFrame(index = index,data =data,columns = cols)
df</code></pre>
<table class="dataframe">
<thead>
<tr>
<th></th>
<th>age</th>
<th>city</th>
</tr>
<tr>
<th>person</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<th>Tom</th>
<td>18</td>
<td>Beijing</td>
</tr>
<tr>
<th>Bob</th>
<td>30</td>
<td>ShangHai</td>
</tr>
<tr>
<th>Mary</th>
<td>25</td>
<td>GuangZhou</td>
</tr>
<tr>
<th>James</th>
<td>40</td>
<td>ShenZhen</td>
</tr>
</tbody>
</table>
<p>这里的主要区别在于我们主动规定了‘name’列为索引。这种把一列默认为索引的方式在excel和sql里都有,异曲同工</p>
<blockquote><strong>从其他数据源读取(网站,excel,mysql等)</strong></blockquote>
<p>我会在第三部分介绍最常用的从excel,csv等读取数据</p>
<p>总体来说,有关创建Dataframe的部分我们不用了解太多,因为实际的场景基本不需要,都是直接从csv,tsv,sql,json等数据源直接获取。</p>
<h2>2. Dataframe 基础操作</h2>
<p>这里我们主要看一下基于索引,行,列,行和列的基础操作,最后会为大家总结一下,现在拿刚刚我们创建过的dataframe为例:</p>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
</tr>
</tbody>
</table>
<h3>2.1 对columns的基础操作</h3>
<blockquote><strong>添加新列</strong></blockquote>
<p>添加一列非常简单:</p>
<pre><code class="python">df['country'] = 'USA'
df
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
<th>country</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
<td>USA</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
<td>USA</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
<td>USA</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
<td>USA</td>
</tr>
</tbody>
</table>
<pre><code class="python">df['adress'] = df['country']
df
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
<th>country</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
<td>USA</td>
<td>USA</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
<td>USA</td>
<td>USA</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
<td>USA</td>
<td>USA</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
<td>USA</td>
<td>USA</td>
</tr>
</tbody>
</table>
<blockquote><strong>修改列中的值</strong></blockquote>
<p>修改一个列的值也是很容易的,我们可以这样做:</p>
<pre><code class="python">df['country'] = 'China'
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
<th>country</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
<td>China</td>
<td>USA</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
<td>China</td>
<td>USA</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
<td>China</td>
<td>USA</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
<td>China</td>
<td>USA</td>
</tr>
</tbody>
</table>
<p>或者稍微多想一步:</p>
<pre><code class="python">
df['adress'] = df['city']+','+ df['country']
df
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
<th>country</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
<td>China</td>
<td>Beijing,China</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
<td>China</td>
<td>ShangHai,China</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
<td>China</td>
<td>GuangZhou,China</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
<td>China</td>
<td>ShenZhen,China</td>
</tr>
</tbody>
</table>
<blockquote><strong>删除列</strong></blockquote>
<p>我们可以应用del或者drop函数,如果是drop,要注意传参时要加上axis = 1.这里简单和大家说明一下axis,这个东西其实就是指轴向,默认的axis=0,是纵向,axis=1是横向</p>
<pre><code class="python">df.drop('country',axis=1)
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>city</th>
<th>name</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Beijing</td>
<td>Tom</td>
<td>Beijing,China</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>ShangHai</td>
<td>Bob</td>
<td>ShangHai,China</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>GuangZhou</td>
<td>Mary</td>
<td>GuangZhou,China</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>ShenZhen</td>
<td>James</td>
<td>ShenZhen,China</td>
</tr>
</tbody>
</table>
<pre><code class="python">del df['city']
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>name</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
<td>Beijing,China</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
<td>ShangHai,China</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
<td>GuangZhou,China</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
<td>ShenZhen,China</td>
</tr>
</tbody>
</table>
<p>这里有一点大家需要注意:</p>
<ul>
<li>drop和del的结果是基于df的两次单独操作,并不是连续的</li>
<li>如果我们执行完drop后,重新查看df,这时你会发现df没有变化,因为没有真正的删除</li>
<li>要想真正删除,需要加上关键字 inplace = True</li>
</ul>
<p>因此如果我们想要连续删除country和city这两列,改进后的代码如下:</p>
<pre><code class="python">df.drop('country',axis=1, inplace=True)
del df['city']
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>name</th>
<th>adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
<td>Beijing,China</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
<td>ShangHai,China</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
<td>GuangZhou,China</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
<td>ShenZhen,China</td>
</tr>
</tbody>
</table>
<blockquote><strong>选取列</strong></blockquote>
<p>这个非常简单,实现代码如下:</p>
<pre><code class="python">df['age'] # 选取age这一列
0 18
1 30
2 25
3 40
Name: age, dtype: int64</code></pre>
<p>或者这样:</p>
<pre><code class="python">df.name
</code></pre>
<pre><code class="python"> 0 Tom
1 Bob
2 Mary
3 James
Name: name, dtype: object</code></pre>
<p>如果想要选取多个列也很容易,传递一个list就行啦:</p>
<pre><code class="python">df[['age','name']]</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>age</th>
<th>name</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
</tr>
</tbody>
</table>
<p>这里注意,和选取单独一列不同,这里我们返回的类型是dataframe,之前的类型是series,我们可以这么理解,一列其实还是一维数组,但是两列及以上是二维的了,当然类型也变了</p>
<p>如果我们想要查看当前的所有列:</p>
<pre><code class="python">df.columns
Out:Index([u'age', u'name', u'adress'], dtype='object')</code></pre>
<p>如果我们想要重新对列进行命名,基本有三种方法,大家挑一种自己喜欢的就行</p>
<ul>
<li>传递list</li>
<li>传递dict</li>
<li>传递 axis</li>
</ul>
<p>用list:</p>
<pre><code class="python">df.columns = ['Age','Name','Adress']
df</code></pre>
<p>用dict:</p>
<pre><code class="python">df.rename(index = str, columns = {'age':'Age','name':'Name','adress':'Adress'}) #这里index=str 有没有都行,我这么做是为了规范
df</code></pre>
<p>用axis:</p>
<pre><code class="python">
df.rename(str.capitalize, axis='columns',inplace =True)
df</code></pre>
<p>最后得到的效果是一样的:</p>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Age</th>
<th>Name</th>
<th>Adress</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
<td>Beijing,China</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
<td>ShangHai,China</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
<td>GuangZhou,China</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
<td>ShenZhen,China</td>
</tr>
</tbody>
</table>
<blockquote><strong>根据条件给列赋值</strong></blockquote>
<p>我们现在想要根据人的年龄新增一列 Group,假设要求如下:</p>
<ul>
<li>18岁以下的是年轻人</li>
<li>18-30的是中年人</li>
<li>30以上的是老年人</li>
</ul>
<p>我们有很多方法可以实现,先看一种,大家可以先忽视loc方法,这里传达的就是基础思路:</p>
<pre><code class="python">df['Group'] = 'elderly'
df.loc[df['Age']<=18, 'Group'] = 'young'
df.loc[(df['Age'] >18) & (df['Age'] <= 30), 'Group'] = 'middle_aged'
df</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Age</th>
<th>Name</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
<td>Beijing,China</td>
<td>young</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
<td>GuangZhou,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<h3>2.2 对 rows 的基础操作</h3>
<blockquote><strong>loc函数 df.loc[row,column]</strong></blockquote>
<p>首先,大多数对于行的操作都可以通过loc函数实现,比如我们想要选取全部的行,除了直接打出df外,可以使用df.loc[:]</p>
<pre><code class="python">df.loc[:]</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Age</th>
<th>Name</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>18</td>
<td>Tom</td>
<td>Beijing,China</td>
<td>young</td>
</tr>
<tr>
<th>1</th>
<td>30</td>
<td>Bob</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>25</td>
<td>Mary</td>
<td>GuangZhou,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>3</th>
<td>40</td>
<td>James</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<blockquote><strong>loc函数条件查询</strong></blockquote>
<pre><code class="python">df.loc[df['Age']>20]
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Name</th>
<th>Age</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>1</th>
<td>Bob</td>
<td>30</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>Mary</td>
<td>25</td>
<td>GuangZhou,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>3</th>
<td>James</td>
<td>40</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<blockquote><strong>loc函数条件行列查询</strong></blockquote>
<pre><code class="python">df.loc[df['Group']=='middle_aged','Name']
1 Bob
2 Mary
Name: Name, dtype: object
</code></pre>
<blockquote><strong>Where 查询</strong></blockquote>
<pre><code class="python">filter_adult = df['Age']>25
result = df.where(filter_adult)
result
</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Name</th>
<th>Age</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>1</th>
<td>Bob</td>
<td>30.0</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
<td>NaN</td>
</tr>
<tr>
<th>3</th>
<td>James</td>
<td>40.0</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<blockquote><strong>Query 筛选</strong></blockquote>
<pre><code class="python"># df.query('Age==30')
df.query('Group=="middle_aged"'and 'Age>30' )</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Name</th>
<th>Age</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody><tr>
<th>3</th>
<td>James</td>
<td>40</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr></tbody>
</table>
<h3>2.3 对 Dataframe 的基础了解</h3>
<p>这里有很多有用的方法,可以帮助到大家大致了解数据的情况:</p>
<pre><code class="python">
df.shape # 了解行列情况
Out:(4, 4)</code></pre>
<pre><code class="python">
df.describe() # 获取可计算列基础统计</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Age</th>
</tr></thead>
<tbody>
<tr>
<th>count</th>
<td>4.000000</td>
</tr>
<tr>
<th>mean</th>
<td>28.250000</td>
</tr>
<tr>
<th>std</th>
<td>9.251126</td>
</tr>
<tr>
<th>min</th>
<td>18.000000</td>
</tr>
<tr>
<th>25%</th>
<td>23.250000</td>
</tr>
<tr>
<th>50%</th>
<td>27.500000</td>
</tr>
<tr>
<th>75%</th>
<td>32.500000</td>
</tr>
<tr>
<th>max</th>
<td>40.000000</td>
</tr>
</tbody>
</table>
<pre><code class="python"># df.head(3) #查看前三行数据,默认为5
df.tail(3) #获得最后三行数据</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Name</th>
<th>Age</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>1</th>
<td>Bob</td>
<td>30</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>Mary</td>
<td>25</td>
<td>GuangZhou,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>3</th>
<td>James</td>
<td>40</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<h2>3. Pandas读取导出数据(csv)</h2>
<p>Pandas 支持大部分常见数据文件读取与存储。一般清楚下,读取文件的方法以 pd.read_ 开头,而写入文件的方法以 pd.to_ 开头。这里我们开始熟悉一下最实用的对于csv文件的读取写入</p>
<blockquote><strong>写入CSV</strong></blockquote>
<pre><code class="python">df.to_csv('person.csv',index=None,sep=',')</code></pre>
<pre><code class="python">import os
os.getcwd()
Out: 'C:\\Users\\E560'</code></pre>
<p>这样大家就可以在'C:UsersE560'的路径下找到我们刚刚生成的csv文件了,这里我把index=None,舍弃了索引值</p>
<blockquote><strong>读取CSV</strong></blockquote>
<p>首先我们确认和python文件同一目录下存在我们刚刚导出的person.csv文件,之后可以很容易的读取了:</p>
<pre><code class="python">person = pd.read_csv('person.csv')
person</code></pre>
<table class="dataframe">
<thead><tr>
<th></th>
<th>Name</th>
<th>Age</th>
<th>Adress</th>
<th>Group</th>
</tr></thead>
<tbody>
<tr>
<th>0</th>
<td>Tom</td>
<td>18</td>
<td>Beijing,China</td>
<td>young</td>
</tr>
<tr>
<th>1</th>
<td>Bob</td>
<td>30</td>
<td>ShangHai,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>2</th>
<td>Mary</td>
<td>25</td>
<td>GuangZhou,China</td>
<td>middle_aged</td>
</tr>
<tr>
<th>3</th>
<td>James</td>
<td>40</td>
<td>ShenZhen,China</td>
<td>elderly</td>
</tr>
</tbody>
</table>
<p>这时我们发现,即使我们读取的csv文件没有索引,但是pandas已经默认帮我们加上了</p>
<h2>4. 总结,资料下载</h2>
<p>好了,现在大家对pandas的使用已经有了基础的印象,我给大家简单总结一下常用的方法:</p>
<blockquote><strong>使用标签选取数据</strong></blockquote>
<pre><code>df.loc[行标签,列标签]
df.loc['Tom':'Mary'] #选取 Tom至Mary的所有行的数据,Tom和Mary是index
df.loc[:,'city'] #选取 city 列的数据
</code></pre>
<p>df.loc 的第一个参数是行标签,第二个参数为列标签(可选参数,默认为所有列标签),两个参数既可以是列表也可以是单个字符,如果两个参数都为列表则返回的是 DataFrame,否则,则为 Series。</p>
<p>PS:loc为location的缩写。</p>
<blockquote><strong>使用位置(index)选取数据</strong></blockquote>
<pre><code>df.iloc[行位置,列位置]
df.iloc[1,1] #选取第二行,第二列的值,返回的为单个值
df.iloc[[0,2],:] #选取第一行及第三行的数据
df.iloc[0:2,:] #选取第一行到第三行(不包含)的数据
df.iloc[:,1] #选取所有记录的第二列的值,返回的为一个Series
df.iloc[1,:] #选取第一行数据,返回的为一个Series
</code></pre>
<p>PS:iloc 则为 integer & location 的缩写</p>
<blockquote><strong>通过逻辑指针进行数据切片</strong></blockquote>
<pre><code>df[逻辑条件]
df[df.age >= 18] #单个逻辑条件
df[(df.age >=18 ) & (df.country=='China') ] #多个逻辑条件组合
</code></pre>
<blockquote><strong>了解并掌握数据大致情况</strong></blockquote>
<table>
<thead><tr>
<th>方法</th>
<th>解释</th>
</tr></thead>
<tbody>
<tr>
<td>count</td>
<td>非na值的数量</td>
</tr>
<tr>
<td>describe</td>
<td>针对Series或个DataFrame列计算汇总统计</td>
</tr>
<tr>
<td>min、max</td>
<td>计算最小值和最大值</td>
</tr>
<tr>
<td>argmin、argmax</td>
<td>计算能够获取到最大值和最小值得索引位置(整数)</td>
</tr>
<tr>
<td>idxmin、idxmax</td>
<td>计算能够获取到最大值和最小值得索引值</td>
</tr>
<tr>
<td>quantile</td>
<td>计算样本的分位数(0到1)</td>
</tr>
<tr>
<td>sum</td>
<td>值的总和</td>
</tr>
<tr>
<td>mean</td>
<td>值得平均数</td>
</tr>
<tr>
<td>median</td>
<td>值得算术中位数(50%分位数)</td>
</tr>
<tr>
<td>mad</td>
<td>根据平均值计算平均绝对离差</td>
</tr>
<tr>
<td>var</td>
<td>样本值的方差</td>
</tr>
<tr>
<td>std</td>
<td>样本值的标准差</td>
</tr>
<tr>
<td>skew</td>
<td>样本值得偏度(三阶矩)</td>
</tr>
<tr>
<td>kurt</td>
<td>样本值得峰度(四阶矩)</td>
</tr>
<tr>
<td>cumsum</td>
<td>样本值得累计和</td>
</tr>
<tr>
<td>cummin,cummax</td>
<td>样本值得累计最大值和累计最小值</td>
</tr>
<tr>
<td>cumprod</td>
<td>样本值得累计积</td>
</tr>
<tr>
<td>diff</td>
<td>计算一阶差分(对时间序列很有用)</td>
</tr>
<tr>
<td>pct_change</td>
<td>计算百分数变化</td>
</tr>
</tbody>
</table>
<blockquote><strong>常见读取写入数据</strong></blockquote>
<table>
<thead><tr>
<th>数据类型</th>
<th>读取</th>
<th>写入</th>
</tr></thead>
<tbody>
<tr>
<td>CSV</td>
<td>read_csv</td>
<td>to_csv</td>
</tr>
<tr>
<td>JSON</td>
<td>read_json</td>
<td>to_json</td>
</tr>
<tr>
<td>HTML</td>
<td>read_html</td>
<td>to_html</td>
</tr>
<tr>
<td>EXCEL</td>
<td>read_excel</td>
<td>to_excel</td>
</tr>
<tr>
<td>SQL</td>
<td>read_sql</td>
<td>to_sql</td>
</tr>
</tbody>
</table>
<p>好了,其实这些就是想要告诉大家如何学习pandas,没有必要了解每一个方法,但是现在想必你知道pandas能实现的功能很多,这样你有具体需求时只要详细查询一下文档即可,接下来几期我们会重点来看pandas里面对于df的各种操作,从简单的数据清理到类似于excel里面的vba,包括cancat,merge,join等等</p>
<blockquote><strong>下载</strong></blockquote>
<p>我把这一期的ipynb文件和py文件放到了GIthub上,大家如果想要下载可以点击下面的链接:</p>
<ul><li>Github仓库地址: <a href="https://link.segmentfault.com/?enc=i%2F1QS2JZRLycFobdxUthZw%3D%3D.gik%2F5cUnx2glhiZCdWPzPdp2ahUj50dmlC8ZB7vzfesyexe4lVw3O6Dvyhvzes06" rel="nofollow">点我下载</a>
</li></ul>
<p>好啦,这一期就讲这么多,希望大家能够继续支持我,完结,撒花</p>
Python 进阶之路 (十一) 再立Flag, 社区最全的itertools深度解析(下)
https://segmentfault.com/a/1190000018268824
2019-02-25T00:29:28+08:00
2019-02-25T00:29:28+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<h2><strong>简单实战</strong></h2>
<p>大家好,我又来了,在经过之前两篇文章的介绍后相信大家对itertools的一些常见的好用的方法有了一个大致的了解,我自己在学完之后仿照别人的例子进行了真实场景下的模拟练习,今天和大家一起分享,有很多部分还可以优化,希望有更好主意和建议的朋友们可以留言哈,让我们一起进步</p>
<h2>实战:分析标准普尔500指数</h2>
<h3>数据源及目标</h3>
<p>在这个例子中,我们首先尝试使用itertools来操作大型数据集:标准普尔500指数的历史每日价格数据。 我会在这个部分的最后附上下载链接和py文件,这里的数据源来自<a href="https://link.segmentfault.com/?enc=4dWYmc%2B1zcVmbf%2BrEkFVDQ%3D%3D.YF3fXN2vdQNIsRw2CdWfUHvi48r8B4a3nFexJoNppn%2BYDpwuLWvUf4s73o6a5X4CFOVHErsEL5%2FzXPApEQEfBMx3fnDA7eD2wggCv3chWGUgDF0wIEUyTPbbRLdJMUvxays%2FgtMf%2Bet21d%2FoJXgaq35fxuM%2FGhg1goeJC2HpUt4tBQ%2ByoFtfJ6zDzZNjM8u4edphrr98LVhdUthH4kS74z7egefVenat%2FIsElFISr4vzrINfY40%2FGX9DR%2BjPxyVuePWmFcMq0jmO6S%2FEF6oekg%3D%3D" rel="nofollow">雅虎财经</a></p>
<blockquote>目标: 找到标准普尔500指数的单日最大收益,最大损失(百分比),和最长的增长周期</blockquote>
<p>首先我们手上得到了 SP500.csv ,让我们对数据有个大概的印象,前十行的数据如下:</p>
<pre><code class="python">Date,Open,High,Low,Close,Adj Close,Volume
1950-01-03,16.660000,16.660000,16.660000,16.660000,16.660000,1260000
1950-01-04,16.850000,16.850000,16.850000,16.850000,16.850000,1890000
1950-01-05,16.930000,16.930000,16.930000,16.930000,16.930000,2550000
1950-01-06,16.980000,16.980000,16.980000,16.980000,16.980000,2010000
1950-01-09,17.080000,17.080000,17.080000,17.080000,17.080000,2520000
1950-01-10,17.030001,17.030001,17.030001,17.030001,17.030001,2160000
1950-01-11,17.090000,17.090000,17.090000,17.090000,17.090000,2630000
1950-01-12,16.760000,16.760000,16.760000,16.760000,16.760000,2970000
1950-01-13,16.670000,16.670000,16.670000,16.670000,16.670000,3330000</code></pre>
<p>为了实现目标,具体思路如下:</p>
<ul>
<li>读取csv文件,并利用 Adj Close这一列转换为每日百分比变化的序列,代表收益,命名为gain</li>
<li>找到gain这一序列中的最大值和最小值,并且找到对应的日期,当然,有可能会出现对应多个日期的情况,我们这里选取日期最近的就好。</li>
<li>定义一个sequence叫做growth_streaks,其中包含了所有 gain中出现的连续为正值的元素组成的tuple,我们要找到这些tuples中长度最长的一个,从而定位其对应的开始时间和结束时间,当然这里也是一样,有可能出现最大长度一样的的情况,这种情况下,我们还是选择日期最近的。</li>
</ul>
<p>这里有关百分比的计算公式如下:</p>
<p><img src="/img/bVboLfG?w=620&h=106" alt="clipboard.png" title="clipboard.png"></p>
<h3>分步实现</h3>
<p>首先在这里,我们会经常处理日期,为了方便后续操作,这里我们引入collections模块的namedtuple来实现对日期的相关操作:</p>
<pre><code class="python">from collections import namedtuple
class DataPoint(namedtuple('DataPoint', ['date', 'value'])):
__slots__ = ()
def __le__(self, other):
return self.value <= other.value
def __lt__(self, other):
return self.value < other.value
def __gt__(self, other):
return self.value > other.value</code></pre>
<p>这里有很多小技巧,之后我会再系统的开一个Python OOP笔记,会为大家都讲到,这里面涉及的小知识点如下:</p>
<ul>
<li>slots :这是一个节省变量内存的好东西,__slot__后面一般都是跟class中 init 方法里面用到的变量,好处在于能够大量节省内存</li>
<li>namedtuple:可以实现类似属性一样调用tuple里面的元素,我在collections里面详细说过,大家可以看看:<a href="https://segmentfault.com/a/1190000018145641">Python 进阶之路 (七) 隐藏的神奇宝藏:探秘Collections</a>
</li>
<li>le:运算符重载,可以得到class中一个变量的长度,必须是整数,也就是说如果传入的是list,dict,tuple,set这些一定没有问题,因为这些序列的长度一定是整数,这里面传递的是tuple()</li>
<li>lt:运算符重载(less than ):可以实现利用 < 比较一个class的不同对象中的值大小的比较</li>
<li>gt:运算符重载(greater than):可以实现利用 > 比较一个class的不同对象中的值大小的比较</li>
</ul>
<p>下面为了唤醒大家的记忆,我这里快速举一个有关于namedtuple,le,lt,gt的小栗子:</p>
<pre><code>from collections import namedtuple
class Person(namedtuple('person', ['name', 'age','city','job'])):
def __le__(self):
return len(self)
def __lt__(self,other):
return self.age < other.age
def __gt__(self,other):
return self.age > other.age
xiaobai = Person('xiaobai', 18, 'paris','student')
laobai = Person('Walter White',52, 'albuquerque','cook')
print('Infomation for first person: ', xiaobai) # 显示全部信息
print('Age of second person is: ', laobai.age) # 根据name得到tuple的数据
print(len(xiaobai))
print(xiaobai > laobai)
print(xiaobai < laobai)
Out: Infomation for first person: Person(name='xiaobai', age=18, city='paris',job='student')
Age of second person is: 52
4
False
True</code></pre>
<p>如果大家对这个例子中的一些地方还有疑问,不用担心,我会在下一个专栏Python OOP学习笔记中和大家慢慢说的<br>。好的,现在回到刚才的实战:</p>
<pre><code>from collections import namedtuple
class DataPoint(namedtuple('DataPoint', ['date', 'value'])):
__slots__ = ()
def __le__(self, other):
return self.value <= other.value
def __lt__(self, other):
return self.value < other.value
def __gt__(self, other):
return self.value > other.value</code></pre>
<p>这里我们的DataPoint类有两个主要属性,一个是datetime类型的日期,一个是当天的标普500值</p>
<p>接下来让我们读取csv文件,并将每行中的Date和Adj Close列中的值存为DataPoint的对象,最后把所有的对象组合为一个sequence序列:</p>
<pre><code>import csv
from datetime import datetime
def read_prices(csvfile, _strptime=datetime.strptime):
with open(csvfile) as infile:
reader = csv.DictReader(infile)
for row in reader:
yield DataPoint(date=_strptime(row['Date'], '%Y-%m-%d').date(),
value=float(row['Adj Close']))
prices = tuple(read_prices('SP500.csv'))</code></pre>
<p>read_prices()生成器打开 SP500.csv 并使用 csv.DictReader()读取数据的每一行。DictReader()将每一行作为 OrderedDict 返回,其中key是每行中的列名。</p>
<p>对于每一行,read_prices()都会生成一个DataPoint对象,其中包含“Date”和“Adj Close”列中的值。 最后,完整的数据点序列作为元组提交给内存并存储在prices变量中</p>
<blockquote><strong>Ps: Ordereddict是我在collections中漏掉的知识点,我马上会补上,大家可以随时收藏<a href="https://segmentfault.com/a/1190000018145641">Python 进阶之路 (七) 隐藏的神奇宝藏:探秘Collections</a>,我会继续更新</strong></blockquote>
<p>接下来我们要把prices这个转变为表达每日价格变化百分比的序列,利用的公式就是刚才提到的,如果忘了的朋友可以往回翻~</p>
<pre><code>gains = tuple(DataPoint(day.date, 100*(day.value/prev_day.value - 1.))
for day, prev_day in zip(prices[1:], prices))</code></pre>
<p>为了得到标普500单日最大涨幅,我们可以用一下方法:</p>
<pre><code>max_gain = DataPoint(None, 0)
for data_point in gains:
max_gain = max(data_point, max_gain)
print(max_gain) # DataPoint(date='2008-10-28', value=11.58)</code></pre>
<p>我们可以把这个方法用之前提到过的reduce简化一下:</p>
<pre><code>import functools as ft
max_gain = ft.reduce(max, gains)
print(max_gain) # DataPoint(date='2008-10-28', value=11.58)</code></pre>
<p>这里有关reduce 和 lambda的用法,我们可以通过一个小栗子来回忆一下:</p>
<pre><code>import functools as ft
x = ft.reduce(lambda x,y:x+y,[1, 2, 3, 4, 5])
print(x)
Out: 15</code></pre>
<p>当然,如果求和在实际场景直接用sum就好,这里只是为了让大家有个印象,如果回忆不起来的老铁们也没有关系,轻轻点击以下链接立刻重温:</p>
<ul>
<li><a href="https://segmentfault.com/a/1190000018114755">Python 进阶之路 (五) map, filter, reduce, zip 一网打尽</a></li>
<li><a href="https://segmentfault.com/a/1190000018121906">Python 进阶之路 (六) 九浅一深 lambda,陈独秀你给我坐下!</a></li>
</ul>
<p>好了,书规正传,我们发现用reduce改进了for循环后得到了同样的结果,单日最大涨幅的日期也一样,但是这里需要注意的是reduce和刚才的for循环完全不是一回事</p>
<p>我们可以想象一下,假如CSV文件中的数据每天都是跌的话。 max_gain最后到底是多少?</p>
<p>在 for 循环中,首先设置max_gain = DataPoint(None,0),因此如果没有涨幅,则最终的max_gain值将是此空 DataPoint 对象。但是,reduce()解决方案会返回最小的单日跌幅,这不是我们想要的,可能会引入一个难以找到的bug</p>
<p>这就是itertools可以帮助到我们的地方。 itertools.filterfalse()函数有两个参数:一个返回True或False的函数,和一个可迭代的输入。它返回一个迭代器,是迭代结果都为False的情况。这里是个小栗子:</p>
<pre><code class="python">import itertools as it
only_positives = it.filterfalse(lambda x: x <= 0, [0, 1, -1, 2, -2])
print(list(only_positives))
Out:[1, 2]
</code></pre>
<p>所以现在我们可以用 itertools.filterfalse()去除掉gains中那些小于0或者为负数的值,这样reduce会仅仅作用在我们想要的正收益上:</p>
<pre><code>max_gain = ft.reduce(max, it.filterfalse(lambda p: p <= 0, gains))
</code></pre>
<p>这里我们默认为gains中一定存在大于0的值,这也是事实,但是如果假设gains中没有的话,我们会报错,因此在使用itertools.filterfalse()的实际场景中要注意到这一点。</p>
<p>针对这种情况,可能你想到的应对方案是在合适的情况下添加TryExpect捕获错误,但是reduce有个更好的解决方案,reuce里面可以传递第三个参数,用做reduce返回结果不存在时的默认值,这一点和字典的get方法有异曲同工之妙,如果对get有疑问的朋友可以回顾我之前的文章:<a href="https://segmentfault.com/a/1190000018102693">Python 进阶之路 (二) Dict 进阶宝典,初二快乐!</a>,还是看一个小栗子:</p>
<pre><code>>>> ft.reduce(max, it.filterfalse(lambda x: x <= 0, [-1, -2, -3]), 0)
0</code></pre>
<p>这回很好理解了,因此我们应用到我们标准普尔指数的实战上:</p>
<pre><code class="python">zdp = DataPoint(None, 0) # zero DataPoint
max_gain = ft.reduce(max, it.filterfalse(lambda p: p.value <= 0, diffs), zdp)</code></pre>
<p>同理,对于标普500单日最大跌幅我们也照猫画虎:</p>
<pre><code>max_loss = ft.reduce(min, it.filterfalse(lambda p: p.value > 0, gains), zdp)
print(max_loss) # DataPoint(date='2018-02-08', value=-20.47)</code></pre>
<p>根据我们的数据源是2018年2月8号那一天,我没有谷歌查询那一天发生了什么,大家感兴趣可以看看哈,但是应该是没有问题的,因为数据源来自雅虎财经</p>
<p>现在我们已经得到了标普500历史上的单日最大涨跌的日期,我们接下来要找到它的最长时间段,其实这个问题等同于在gains序列中找到最长的连续为正数的点的集合,itertools.takewhile()和itertools.dropwhile()函数非常适合处理这种情况。</p>
<p>itertools.takewhile()接受两个参数,一个为判断的条件,一个为可迭代的序列,会返回第一个判断结果为False时之前的迭代过的所有元素,下面的小栗子很好的解释了这一点</p>
<pre><code>it.takewhile(lambda x: x < 3, [0, 1, 2, 3, 4]) # 0, 1, 2</code></pre>
<p>itertools.dropwhile() 则恰恰相反:</p>
<pre><code>it.dropwhile(lambda x: x < 3, [0, 1, 2, 3, 4]) # 3, 4
</code></pre>
<p>因此我们可以创建一下方法来实现在gains中找到连续为正数的序列:</p>
<pre><code>def consecutive_positives(sequence, zero=0):
def _consecutives():
for itr in it.repeat(iter(sequence)):
yield tuple(it.takewhile(lambda p: p > zero,
it.dropwhile(lambda p: p <= zero, itr)))
return it.takewhile(lambda t: len(t), _consecutives())
growth_streaks = consecutive_positives(gains, zero=DataPoint(None, 0))
longest_streak = ft.reduce(lambda x, y: x if len(x) > len(y) else y,
growth_streaks)</code></pre>
<p>最后让我们看一下完整的代码:</p>
<pre><code class="python">from collections import namedtuple
import csv
from datetime import datetime
import itertools as it
import functools as ft
class DataPoint(namedtuple('DataPoint', ['date', 'value'])):
__slots__ = ()
def __le__(self, other):
return self.value <= other.value
def __lt__(self, other):
return self.value < other.value
def __gt__(self, other):
return self.value > other.value
def consecutive_positives(sequence, zero=0):
def _consecutives():
for itr in it.repeat(iter(sequence)):
yield tuple(it.takewhile(lambda p: p > zero,
it.dropwhile(lambda p: p <= zero, itr)))
return it.takewhile(lambda t: len(t), _consecutives())
def read_prices(csvfile, _strptime=datetime.strptime):
with open(csvfile) as infile:
reader = csv.DictReader(infile)
for row in reader:
yield DataPoint(date=_strptime(row['Date'], '%Y-%m-%d').date(),
value=float(row['Adj Close']))
# Read prices and calculate daily percent change.
prices = tuple(read_prices('SP500.csv'))
gains = tuple(DataPoint(day.date, 100*(day.value/prev_day.value - 1.))
for day, prev_day in zip(prices[1:], prices))
# Find maximum daily gain/loss.
zdp = DataPoint(None, 0) # zero DataPoint
max_gain = ft.reduce(max, it.filterfalse(lambda p: p.value <= zdp, gains))
max_loss = ft.reduce(min, it.filterfalse(lambda p: p.value > zdp, gains), zdp)
# Find longest growth streak.
growth_streaks = consecutive_positives(gains, zero=DataPoint(None, 0))
longest_streak = ft.reduce(lambda x, y: x if len(x) > len(y) else y,
growth_streaks)
# Display results.
print('Max gain: {1:.2f}% on {0}'.format(*max_gain))
print('Max loss: {1:.2f}% on {0}'.format(*max_loss))
print('Longest growth streak: {num_days} days ({first} to {last})'.format(
num_days=len(longest_streak),
first=longest_streak[0].date,
last=longest_streak[-1].date
))</code></pre>
<p>最终结果如下:</p>
<pre><code>Max gain: 11.58% on 2008-10-13
Max loss: -20.47% on 1987-10-19
Longest growth streak: 14 days (1971-03-26 to 1971-04-15)</code></pre>
<p>数据源可以点击<a href="https://link.segmentfault.com/?enc=hGvLfwfCfmRwvsS21xbkyw%3D%3D.Pb500jO7LRSToGE0Y4c%2Bbl%2Bo5Oj4PwcduPAI2JnHUdYTPpgLBtFkgxCtwsMrbbLKcX%2BQYF%2F%2FtBBc3JV6%2FYCyOjNYvMuoKcundwE1WQJQJLx8hNWUiPgLxCwfCS8MpRtl" rel="nofollow">这里</a>下载</p>
<h2>总结</h2>
<p>这次我为大家梳理一个利用itertools进行了简单实战的小栗子,这里我们旨在多深入了解itertools,但是真实的生活中,遇到这种问题,哪有这么麻烦,一个pandas包就搞定了,我以后会和大家分享和pandas有关的知识,这一次接连三期的itertools总结希望大家喜欢。</p>
<p>itertools深度解析至此全剧终。</p>
Python 进阶之路 (十) 再立Flag, 社区最全的itertools深度解析(中)
https://segmentfault.com/a/1190000018242976
2019-02-22T02:10:06+08:00
2019-02-22T02:10:06+08:00
alpha94511
https://segmentfault.com/u/alpha94511
3
<h2>前情回顾</h2>
<p>大家好,我又回来了。今天我会继续和大家分享itertools这个神奇的自带库,首先,让我们回顾一下上一期结尾的时候我们讲到的3个方法:</p>
<ul>
<li><strong>combinations()</strong></li>
<li><strong>combinations_with_replacement()</strong></li>
<li><strong>permutations()</strong></li>
</ul>
<p>让我们对这3个在排列组合中经常会使用到的函数做个总结</p>
<h4><strong><em>combinations()</em></strong></h4>
<blockquote>基础概念</blockquote>
<ul>
<li>模板:combinations(iterable, n)</li>
<li>参数:iterable为可迭代的对象(list,tuple...), n为想要的组合包含的元素数</li>
<li>返回值: 返回在iterable里n个元素组成的tuple的全部组合(不考虑顺序,元素自身不可重复)</li>
</ul>
<blockquote>应用实例</blockquote>
<pre><code class="python">
import itertools as it
lst = [1,2,3]
result = list(it.combinations(lst,2))
print(result)
Out: [(1, 2), (1, 3), (2, 3)]
</code></pre>
<p>这里我们从lst这个list里面选取所有由两个元素组成的组合,得到结果如图所示,这里没有考虑顺序,因此我们不会看到(1,2)和(2,1)被算作两种组合,元素自身不可重复,所以没有(1,1),(2,2),(3,3)的组合出现</p>
<h4><strong><em>combinations_with_replacement()</em></strong></h4>
<blockquote>基础概念</blockquote>
<ul>
<li>模板:combinations_with_replacement(iterable, n)</li>
<li>参数:iterable为可迭代的对象(list,tuple...), n为想要的组合包含的元素数</li>
<li>返回值: 返回在iterable里n个元素组成的tuple的全部组合(不考虑顺序,元素自身可重复)</li>
</ul>
<blockquote>应用实例</blockquote>
<pre><code class="python">
import itertools as it
lst = [1,2,3]
result = list(it.combinations_with_replacement(lst,2))
print(result)
Out: [(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
</code></pre>
<p>和刚才的区别是多了(1,1),(2,2),(3,3)的组合,也就是说允许每个元素自己和自己组合</p>
<h4><strong><em>permutations()</em></strong></h4>
<blockquote>基础概念</blockquote>
<ul>
<li>模板:permutations(iterable, n=None)</li>
<li>参数:iterable为可迭代的对象(list,tuple...), n为想要的组合包含的元素数</li>
<li>返回值: 返回在iterable里n个元素组成的tuple的全部组合(考虑顺序,元素自身不可重复)</li>
</ul>
<blockquote>应用实例</blockquote>
<pre><code class="python">
import itertools as it
lst = [1,2,3]
result = list(it.permutations(lst,2))
print(result)
Out: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
</code></pre>
<p>我们用permutations得到的结果是自身元素不能重复的情况下,一个iterable里面由n个元素构成的全部组合,考虑顺序</p>
<h4><strong><em>不同点汇总</em></strong></h4>
<p>我们这里可以简单汇总一下三个函数的不同点,汇总一张精华满满的表格送个大家,希望大家如果日后有一天需要用到的话可以回来我这里看看,顺便给勤劳的博主点个赞也是好的👍</p>
<table>
<thead><tr>
<th>函数</th>
<th>组合的元素个数</th>
<th>示例 list</th>
<th>输出结果</th>
<th>特点</th>
</tr></thead>
<tbody>
<tr>
<td>combinations()</td>
<td>2</td>
<td>[1,2,3]</td>
<td>(1, 2), (1, 3), (2, 3)</td>
<td>元素自身不能重复,不考虑顺序</td>
</tr>
<tr>
<td>combinations_with_replacement()</td>
<td>2</td>
<td>[1,2,3]</td>
<td>(1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)</td>
<td>元素自身能重复,不考虑顺序</td>
</tr>
<tr>
<td>permutations()</td>
<td>2</td>
<td>[1,2,3]</td>
<td>(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)</td>
<td>元素自身不能重复,考虑顺序</td>
</tr>
</tbody>
</table>
<p><strong>我必须吐槽一下sf的这个markdown,连个表格的快捷方式都没有么。。。。</strong></p>
<h2>数字序列</h2>
<p>在完美的收尾了上一期的内容后我们可以继续前进了,使用itertools,我们可以轻松地在无限序列上生成迭代器。接下来我们主要看看和数字序列相关的方法和功能。</p>
<p>首先我们来看一个生成奇数偶数的例子,如果不知道itertools的情况下,我们利用生成器的解决方案如下:</p>
<pre><code class="python">def evens():
"""Generate even integers, starting with 0."""
n = 0
while True:
yield n
n += 2
def odds():
"""Generate odd integers, starting with 1."""
n = 1
while True:
yield n
n += 2
evens = evens()
even_numbers = list(next(evens) for _ in range(5))
print(even_numbers)
odds = odds()
odd_numbers = list(next(odds) for _ in range(5))
print(odd_numbers)
Out:[0, 2, 4, 6, 8]
[1, 3, 5, 7, 9]
</code></pre>
<p>现在我们可以利用itertools里面的it.count()方法进行优化:</p>
<pre><code class="python">import itertools as it
evens = it.count(step=2)
even_numbers = list(next(evens) for _ in range(5))
print(even_numbers)
odds = it.count(start=1, step=2)
odd_numbers = list(next(odds) for _ in range(5))
print(odd_numbers)
Out:[0, 2, 4, 6, 8]
[1, 3, 5, 7, 9]</code></pre>
<p>itertools.count()这个方法主要就是用来计数,默认从0开始,我们可以通过设置start关键字参数从任何数字开始计数,默认为0.同样也可以设置step关键字参数来确定从count()返回的数字之间的间隔,默认为1。</p>
<p>再来看其它两个用到itertools count方法的例子:</p>
<pre><code class="python">>>> count_with_floats = it.count(start=0.5, step=0.75)
>>> list(next(count_with_floats) for _ in range(5))
[0.5, 1.25, 2.0, 2.75, 3.5]</code></pre>
<p>可以用来计算float类型</p>
<pre><code class="python">>>> negative_count = it.count(start=-1, step=-0.5)
>>> list(next(negative_count) for _ in range(5))
[-1, -1.5, -2.0, -2.5, -3.0]</code></pre>
<p>或是负数也没有问题</p>
<blockquote>在某些方面,count()类似于内置range()函数,但count()总是返回无限序列。<br>无限序列的好处在于它不可能完全迭代</blockquote>
<p>count()函数甚至模拟了内置的enumrate()功能:</p>
<pre><code class="python">import itertools as it
print(list(zip(it.count(), ['a', 'b', 'c'])))
Out:[(0, 'a'), (1, 'b'), (2, 'c')]
</code></pre>
<h4><strong>其他有意思的方法</strong></h4>
<blockquote><strong><em>repeat(object, times=1)</em></strong></blockquote>
<p>首先让我们看一下itertools里面的repeat放法,它的功能是返回一个值的无限序列:</p>
<pre><code class="python">all_ones = it.repeat(1) # 1, 1, 1, 1, ...
all_twos = it.repeat(2) # 2, 2, 2, 2, ...</code></pre>
<p>如果我们希望可以指定返回序列的长度,我们可以在方法的第二个参数加上想要的序列长度即可:</p>
<pre><code class="python">five_ones = it.repeat(1, 5) # 1, 1, 1, 1, 1
three_fours = it.repeat(4, 3) # 4, 4, 4</code></pre>
<blockquote><strong><em>cycle(iterable)</em></strong></blockquote>
<p>接着估计你可能想到了,那如果我们想要不断循环两个数呢?答案是itertools的cycle方法:</p>
<pre><code>alternating_ones = it.cycle([1, -1]) # 1, -1, 1, -1, 1, -1, ...</code></pre>
<p>如果你想要输出上面的alternating_ones是不可能的,因为这是一个无限序列,你会收到下面的错误:</p>
<pre><code>Traceback (most recent call last):
File "C:\Users\Desktop\itertools.py", line 48, in <module>
alternating_ones = list(it.cycle([0, 1]))
MemoryError</code></pre>
<blockquote><strong><em>accumulate(iterable, func=operator.add)</em></strong></blockquote>
<p>itertools.accumulate()函数, 这个函数有些特殊,它接受两个参数 :</p>
<ul>
<li>一个可迭代的输入</li>
<li>一个二进制函数func(即一个具有两个输入的函数)</li>
</ul>
<p>并返回一个迭代器,用于将func应用于输入元素的累积结果。看一个小栗子:</p>
<pre><code class="python">>>> import operator
>>> list(it.accumulate([1, 2, 3, 4, 5], operator.add))
[1, 3, 6, 10, 15]</code></pre>
<p>accumulate()返回的迭代器中的第一个值始终是输入序列中的第一个值。在这个例子中,是1,因为1是 [1,2,3,4,5]中的第一个值。<br>输出迭代器中的下一个值是输入序列的前两个元素的总和:add(1,2)= 3,所以操作模式如下:</p>
<ul><li><strong><em>add(3, 3) = add(add(1, 2), 3) = 6</em></strong></li></ul>
<p>以此类推,最终得到最后的答案。实际上accumulate()的第二个参数本身就是默认为operator.add(),因此前面的示例可以简化为:</p>
<pre><code class="python">>>> list(it.accumulate([1, 2, 3, 4, 5]))
[1, 3, 6, 10, 15]
</code></pre>
<p>我们也可以自己添加别的方法到第二个参数里:</p>
<pre><code class="python">>>> list(it.accumulate([9, 21, 17, 5, 11, 12, 2, 6], min))
[9, 9, 9, 5, 5, 5, 2, 2]</code></pre>
<p>好啦,itertools 有关于数字序列方面的方法我就简单介绍到这里啦,有需要的朋友可以自己去看看,其实还是非常实用的,不是类似lambda那些花哨的方法</p>
<h2>对List和字符串的相关操作</h2>
<blockquote>itertools.product 实现交叉组合</blockquote>
<pre><code class="python">
>>> product([1, 2], ['a', 'b'])
(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')
</code></pre>
<p>此处实现两个可迭代序列的元素组合。</p>
<blockquote>itertools.tee 从一个输入序列生成任意数量的生成器</blockquote>
<pre><code>>>> iter1, iter2 = it.tee(['a', 'b', 'c'], 2)
>>> list(iter1)
['a', 'b', 'c']
>>> list(iter2)
['a', 'b', 'c']</code></pre>
<p>注意这里的iter1和iter2相互不会一影响。是一个深复制</p>
<blockquote>islice(iterable, stop) islice(iterable, start, stop, step=1) 切片</blockquote>
<pre><code>
>>> islice([1, 2, 3, 4], 3)
1, 2, 3
>>> islice([1, 2, 3, 4], 1, 2)
2, 3</code></pre>
<p>这里和list最大的区别在于返回对象是一个迭代器,并不是一个list,islice(iterable, stop)中stop是切片截至的index,和list切片一样,并不会包括stop本身的值。如果想要指定切片起始位置和不长,就使用islice(iterable, start, stop, step=1)</p>
<blockquote>chain(*iterables)</blockquote>
<pre><code>>>> chain('abc', [1, 2, 3]) #<type 'itertools.chain'>
'a', 'b', 'c', 1, 2, 3
</code></pre>
<p>返回一个链对象,其__next __()方法返回第一个iterable中的元素,直到它耗尽,然后是来自下一个iterable的元素,直到所有的iterables都用完为止。</p>
<p>这里有一点需要注意,chain()函数有一个类方法.from_iterable(),它将一个iterable作为参数。迭代的元素本身必须是可迭代的,因此效应是chain.from_iterable()某种程度上可以实现类似 list.extend() 或者 list.append() 的功能, 我们一起看一个混合了很多itertools中其他方法的综合小栗子:</p>
<pre><code>import itertools as it
cycle = it.chain.from_iterable(it.repeat('abc'))
result = list(it.islice(cycle,8))
print(result)
Out: ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b']
</code></pre>
<p>这里其实it.chain.from_iterable里面甚至可以放进一个无限序列,不一定是定长的。</p>
<h2>总结</h2>
<p>这一期的内容没有那么长,我们简单了解了一下itertools的基础好用的方法,下一期就是简单实战了,我自己在网上找了一些非常不错的案例,照猫画虎练习了一下,打算在下一期和大家一起分享。这次的文章就到这里啦,我们下期见!!!</p>
Python 进阶之路 (九) 再立Flag, 社区最全的itertools深度解析(上)
https://segmentfault.com/a/1190000018224910
2019-02-20T20:10:19+08:00
2019-02-20T20:10:19+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<h2>前言</h2>
<p>大家好,今天想和大家分享一下我的itertools学习体验及心得,itertools是一个Python的自带库,内含多种非常实用的方法,我简单学习了一下,发现可以大大提升工作效率,在sf社区内没有发现十分详细的介绍,因此希望想自己做一个学习总结。也和朋友们一起分享一下心得</p>
<p>首先,有关itertools的详细介绍,我参考的是Python 3.7官方文档:<a href="https://link.segmentfault.com/?enc=9ocmSeacn2GfQ88g2lgcqw%3D%3D.%2F5SMMTeaqm9ZCXw4HohFm76V4P5u28JtfmXnTLAgLxvARjPe7qJUfVWa0pKHurOrPZRTdmnBSBqxmxW5%2BmuRzQ%3D%3D" rel="nofollow">itertools — Functions creating iterators for efficient looping</a>,大家感兴趣可以去看看,目前还没有中文版本,十分遗憾,这里不得不吐槽一句,为啥有日语,韩语,中文的版本没有跟上呢?</p>
<p><img src="/img/bVbowcF?w=1254&h=203" alt="clipboard.png" title="clipboard.png"></p>
<p>书规正传,itertools 我个人评价是Python3里最酷的东西! 如果你还没有听说过它,那么你就错过了Python 3标准库的一个最大隐藏宝藏,是的,我很快就抛弃了刚刚分享的collections模块:<a href="https://segmentfault.com/a/1190000018145641">Python 进阶之路 (七) 隐藏的神奇宝藏:探秘Collections</a>,毕竟男人都是大猪蹄子</p>
<p>网上有很多优秀的资源可用于学习itertools模块中的功能。但对我而言,官方文档本身总是一个很好的起点。学会做甜点之前,总是要会最基础的面包。这篇文章便是基本基于文档归纳整理而来。</p>
<p>我在学习后的整体感受是,关于itertools只知道它包含的函数是远远不够的。真正的强大之处在于组合这些功能以创建<strong>快速,占用内存效率极少,漂亮优雅</strong>的代码。</p>
<p>在这篇很长的文章里,我会全面复盘我的学习历程,争取全面复制每一个细节,在开始之前,如果朋友们还不太知道迭代器和生成器是什么,可以参考以下科普扫盲:</p>
<ul>
<li><a href="https://link.segmentfault.com/?enc=HFWi5sHnjk40H7o%2Fr4bM7A%3D%3D.CcRLsIjuEIWPjrgExLsk4R2%2Fyc4sqn3jHDB59HPmeIWDYrmXQkW8A5WJEhxEP7lURYc024n9HLvyaBcCHpSvZQ%3D%3D" rel="nofollow">菜鸟教程 迭代器生成器</a></li>
<li><a href="https://link.segmentfault.com/?enc=pfeH7kRWFjqLFGAa7yCvQQ%3D%3D.tC%2FBzkAUfrqXrZGT43DITln2JGynfsCDrHDA7qRLUFZd2jM6eYcFr7%2FeEqcB0Te%2FDZuzMo1FErNSIhSI2IpPZDL01SYxxLv8JPbujQKCh8%2FTdQlps1M1iKIcFqwV%2BcHlqjSvJwcdgWsCS8CgRFEY5jZeJwmeGkUBZlSHqHMQHeI25DPzxWs672AIVSdqzasz" rel="nofollow">廖雪峰的迭代器讲解</a></li>
<li><a href="https://link.segmentfault.com/?enc=h9dHBVGDveiqDV8VrJ%2Bw%2Bw%3D%3D.TsUtfW%2F5u2sChgQJLzAVeUJnwJKry4Qpnd4nX9n0ts1UYa14jNTORxdmtXsrhjXPp5%2BkQUXg%2B5f%2FxsiaQlWS2Jsz%2FMpTBvHyjb8Ei0ek30K4kNkgSLgxv5sAqAv9cv%2FYUB3RHfUk42QLsQU2mBNn6NLFyHjuowTgOATPNp1BnMz1wnRZ0Of6lmbQcrKN91%2Be" rel="nofollow">廖雪峰的生成器讲解</a></li>
</ul>
<h2>神奇的itertools</h2>
<p>好啦,坐好扶稳,我们准备上车了,根据官方文档的定义:</p>
<blockquote>This module implements a number of iterator building blocks inspired by constructs from APL, Haskell, and SML. Each has been recast in a form suitable for Python.</blockquote>
<p>翻译过来大概就是它是一个实现了许多迭代器构建的模块,它们受到来自APL,Haskell和SML的构造的启发......可以提高效率啥的,</p>
<p>这主要意味着itertools中的函数是在迭代器上“操作”以产生更复杂的迭代器。<br>例如,考虑内置的zip()函数,该函数将任意数量的iterables作为参数,并在其相应元素的元组上返回迭代器:</p>
<pre><code>print(list(zip([1, 2, 3], ['a', 'b', 'c'])))
Out:[(1, 'a'), (2, 'b'), (3, 'c')]
</code></pre>
<p>这里让我们思考一个问题,这个zip函数到底是如何工作的?</p>
<p>与所有其他list一样,[1,2,3] 和 ['a','b','c'] 是可迭代的,这意味着它们可以一次返回一个元素。<br>从技术上讲,任何实现:</p>
<ul>
<li><strong><em>.__ iter __()</em></strong></li>
<li>或<strong>.__ getitem __()</strong>
</li>
</ul>
<p>方法的Python对象都是可迭代的。如果对这方面有疑问,大家可以看前言部分提到的教程哈</p>
<p>其实有关iter()这个内置函数,当在一个list或其他可迭代的对象 x 上调用时,会返回x自己的迭代器对象:</p>
<pre><code>iter([1, 2, 3, 4])
iter((1,2,3,4))
iter({'a':1,'b':2})
Out:<list_iterator object at 0x00000229E1D6B940>
<tuple_iterator object at 0x00000229E3879A90>
<dict_keyiterator object at 0x00000229E1D6E818></code></pre>
<p>实际上,zip()函数通过在每个参数上调用iter(),然后使用next()推进iter()返回的每个迭代器并将结果聚合为元组来实现。 zip()返回的迭代器遍历这些元组</p>
<p>而写到这里不得不回忆一下,之前在 <a href="https://segmentfault.com/a/1190000018114755">Python 进阶之路 (五) map, filter, reduce, zip 一网打尽</a>我给大家介绍的神器map()内置函数,其实某种意义上也是一个迭代器的操作符而已,它以最简单的形式将单参数函数一次应用于可迭代的sequence的每个元素:</p>
<ul><li>模板: map(func,sequence)</li></ul>
<pre><code>list(map(len, ['xiaobai', 'at', 'paris']))
Out: [7, 2, 5]</code></pre>
<p>参考map模板,不难发现:map()函数通过在sequence上调用iter(),使用next()推进此迭代器直到迭代器耗尽,并将func 应用于每步中next()返回的值。在上面的例子里,在['xiaobai', 'at', 'paris']的每个元素上调用len(),从而返回一个迭代器包含list中每个元素的长度</p>
<p>由于迭代器是可迭代的,因此可以用 zip()和 map()在多个可迭代中的元素组合上生成迭代器。<br>例如,以下对两个list的相应元素求和:</p>
<pre><code>a = [1, 2, 3]
b = [4, 5, 6]
list(map(sum, zip(a,b)))
Out: [5, 7, 9]
</code></pre>
<p>这个例子很好的解释了如何构建itertools中所谓的 <strong><em>“迭代器代数”</em></strong> 的函数的含义。我们可以把itertools视为一组构建砖块,可以组合起来形成专门的“数据管道”,就像这个求和的例子一样。</p>
<blockquote><em>其实在Python 3里,如果我们用过了map() 和 zip() ,就已经用过了itertools,因为这两个函数返回的就是迭代器!</em></blockquote>
<p>我们使用这种 itertools 里面所谓的 <strong><em>“迭代器代数”</em></strong> 带来的好处有两个:</p>
<ol>
<li><strong>提高内存效率 (lazy evaluation)</strong></li>
<li><strong>提速</strong></li>
</ol>
<p>可能有朋友对这两个好处有所疑问,不要着急,我们可以分析一个具体的场景:</p>
<blockquote>现在我们有一个list和正整数n,编写一个将list 拆分为长度为n的组的函数。为简单起见,假设输入list的长度可被n整除。例如,如果输入= [1,2,3,4,5,6] 和 n = 2,则函数应返回 [(1,2),(3,4),(5,6)]。</blockquote>
<p>我们首先想到的解决方案可能如下:</p>
<pre><code>def naive_grouper(lst, n):
num_groups = len(lst) // n
return [tuple(lst[i*n:(i+1)*n]) for i in range(num_groups)]</code></pre>
<p>我们进行简单的测试,结果正确:</p>
<pre><code>nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
naive_grouper(nums, 2)
Out: [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
</code></pre>
<p><strong>但是问题来了,如果我们试图传递一个包含1亿个元素的list时会发生什么?我们需要大量内存!即使有足够的内存,程序也会挂起一段时间,直到最后生成结果</strong></p>
<p>这个时候如果我们使用itertools里面的迭代器就可以大大改善这种情况:</p>
<pre><code>def better_grouper(lst, n):
iters = [iter(lst)] * n
return zip(*iters)</code></pre>
<p>这个方法中蕴含的信息量有点大,我们现在拆开一个个看,表达式 [iters(lst)] * n 创建了对同一迭代器的n个引用的list:</p>
<pre><code>nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
iters = [iter(nums)] * 2
list(id(itr) for itr in iters) # Id 没有变化,就是创建了n个索引
Out: [1623329389256, 1623329389256]
</code></pre>
<p>接下来,zip(* iters)在 iters 中的每个迭代器的对应元素对上返回一个迭代器。当第一个元素1取自“第一个”迭代器时,“第二个”迭代器现在从2开始,因为它只是对“第一个”迭代器的引用,因此向前走了一步。因此,zip()生成的第一个元组是(1,2)。</p>
<p>此时,iters中的所谓 “两个”迭代器从3开始,所以当zip()从“first”迭代器中拉出3时,它从“second”获得4以产生元组(3,4)。这个过程一直持续到zip()最终生成(9,10)并且iters中的“两个”迭代器都用完了:</p>
<blockquote><strong>注意: 这里的"第一个","第二个" ,"两个"都是指向一个迭代器,因为id没有任何变化!!</strong></blockquote>
<p>最后我们发现结果是一样的:</p>
<pre><code>nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list(better_grouper(nums, 2))
Out: [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]</code></pre>
<p>但是,这里我做了测试,发现二者的消耗内存是天壤之别,而且在使用iter+zip()的组合后,执行速度快了500倍以上,大家感兴趣可以自己测试,把 nums 改成 xrange(100000000) 即可</p>
<p>现在让我们回顾一下刚刚写好的better_grouper(lst, n) 方法,不难发现,这个方法存在一个明显的缺陷:如果我们传递的n不能被lst的长度整除,执行时就会出现明显的问题:</p>
<pre><code>>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> list(better_grouper(nums, 4))
[(1, 2, 3, 4), (5, 6, 7, 8)]</code></pre>
<p>我们发现分组输出中缺少元素9和10。发生这种情况是因为一旦传递给它的最短的迭代次数耗尽,zip()就会停止聚合元素。而我们想要的是不丢失任何元素。因此解决办法是我们可以使用 <strong>itertools.zip_longest()</strong> 它可以接受任意数量的 iterables 和 fillvalue 这个关键字参数,默认为None。我们先看一个简单实例</p>
<pre><code>>>> import itertools as it
>>> x = [1, 2, 3, 4, 5]
>>> y = ['a', 'b', 'c']
>>> list(zip(x, y)) # zip总是执行完最短迭代次数停止
[(1, 'a'), (2, 'b'), (3, 'c')]
>>> list(it.zip_longest(x, y))
[(1, 'a'), (2, 'b'), (3, 'c'), (4, None), (5, None)]
</code></pre>
<p>这个例子已经非常清晰的体现了zip()和 zip_longest()的区别,现在我们可以优化 better_grouper 方法了:</p>
<pre><code>import itertools as it
def grouper(lst, n, fillvalue=None):
iters = [iter(lst)] * n
return it.zip_longest(*iters, fillvalue=fillvalue) # 默认就是None
</code></pre>
<p>我们再来看优化后的测试:</p>
<pre><code>>>> nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> print(list(grouper(nums, 4)))
[(1, 2, 3, 4), (5, 6, 7, 8), (9, 10, None, None)]</code></pre>
<p>已经非常理想了,各位老铁们可能还没有意识到,<strong><em>我们刚刚所做的一切就是创建itertools 里面grouper方法的全过程!</em></strong></p>
<p>现在让我们看看真正的 <a href="https://link.segmentfault.com/?enc=hjzxyNKZoMPC5Txu0RnY6g%3D%3D.9xa8WsyJmE1YU9eoiwf6k3PqO0QgBELsqqYHYzBHApKFlANlz11mLOxp12yDFppB52G%2FwTSO8atn3CxQAiNDbOkPnTM8wBhSS0sjPzg0vRg%3D" rel="nofollow">官方文档</a> 里面所写的grouper方法:</p>
<p><img src="/img/bVboC6q?w=585&h=131" alt="clipboard.png" title="clipboard.png"></p>
<p>和我们写的基本一样,除了可以接受多个iterable 参数,用了*args</p>
<p>最后心满意足的直接调用一下:</p>
<p><img src="/img/bVboC71?w=651&h=410" alt="clipboard.png" title="clipboard.png"></p>
<p>输出结果如下:</p>
<p><img src="/img/bVboC75?w=888&h=94" alt="clipboard.png" title="clipboard.png"></p>
<h2>暴力求解(brute force)</h2>
<p>首先基础概念扫盲,所谓暴力求解是算法中的一种,简单来说就是 <strong><em>利用枚举所有的情况,或者其它大量运算又不用技巧的方式,来求解问题的方法。</em></strong><br>我在看过暴力算法的广义概念后,首先想到的居然是盗墓笔记中的王胖子</p>
<blockquote>如果有看过盗墓笔记朋友,你会发现王胖子其实是一个推崇暴力求解的人,在无数次遇到困境时祭出的”枚举法“,就是暴力求解,例如我印象最深的是云顶天宫中,一行人被困在全是珠宝的密室中无法逃脱,王胖子通过枚举排除所有可能性,直接得到”身边有鬼“ 的最终解。<p><strong>PS: 此处致敬南派三叔,和那些他填不上的坑</strong></p>
</blockquote>
<p>扯远了,回到现实中来,我们经常会碰到如下的经典题目:</p>
<blockquote>你有三张20美元的钞票,五张10美元的钞票,两张5美元的钞票和五张1美元的钞票。可以通过多少种方式得到100美元?</blockquote>
<p>为了暴力破解这个问题,我们只要把所有组合的可能性罗列出来,然后找出100美元的组合即可,首先,让我们创建一个list,包含我们手上所有的美元:</p>
<pre><code>bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
</code></pre>
<p>这里itertools会帮到我们。 <strong>itertools.combinations()</strong> 接受两个参数</p>
<ul>
<li>一个可迭代的input</li>
<li>正整数n</li>
</ul>
<p>最终会在 input中 n 个元素的所有组合的元组上产生一个迭代器。</p>
<pre><code>import itertools as it
bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
result =list(it.combinations(bills, 3))
print(len(result)) # 455种组合
print(result)
Out: 455
[(20, 20, 20), (20, 20, 10), (20, 20, 10), ... ]</code></pre>
<p>我仅剩的高中数学知识告诉我其实这个就是一个概率里面的 C 15(下标),3(上标)问题,好了,现在我们拥有了各种组合,那么我们只需要在各种组合里选取总数等于100的,问题就解决了:</p>
<pre><code>makes_100 = []
for n in range(1, len(bills) + 1):
for combination in it.combinations(bills, n):
if sum(combination) == 100:
makes_100.append(combination)</code></pre>
<p>这样得到的结果是包含重复组合的,我们可以在最后直接用一个set过滤掉重复值,最终得到答案:</p>
<pre><code>import itertools as it
bills = [20, 20, 20, 10, 10, 10, 10, 10, 5, 5, 1, 1, 1, 1, 1]
makes_100 = []
for n in range(1, len(bills) + 1):
for combination in it.combinations(bills, n):
if sum(combination) == 100:
makes_100.append(combination)
print(set(makes_100))
Out:{(20, 20, 10, 10, 10, 10, 10, 5, 1, 1, 1, 1, 1),
(20, 20, 10, 10, 10, 10, 10, 5, 5),
(20, 20, 20, 10, 10, 10, 5, 1, 1, 1, 1, 1),
(20, 20, 20, 10, 10, 10, 5, 5),
(20, 20, 20, 10, 10, 10, 10)}
</code></pre>
<p>所以最后我们发现一共有5种方式。 现在让我们把题目换一种问法,就完全不一样了:</p>
<blockquote>现在要把100美元的钞票换成零钱,你可以使用任意数量的50美元,20美元,10美元,5美元和1美元钞票,有多少种方法?</blockquote>
<p>在这种情况下,我们没有预先设定的钞票数量,因此我们需要一种方法来使用任意数量的钞票生成所有可能的组合。为此,我们需要用到<strong>itertools.combinations_with_replacement()</strong>函数。</p>
<p>它就像combination()一样,接受可迭代的输入input 和正整数n,并从输入返回有n个元组的迭代器。不同之处在于combination_with_replacement()允许元素在它返回的元组中重复,看一个小栗子:</p>
<pre><code>>>> list(it.combinations_with_replacement([1, 2], 2)) #自己和自己的组合也可以
[(1, 1), (1, 2), (2, 2)]
</code></pre>
<p>对比 itertools.combinations():</p>
<pre><code>>>> list(it.combinations([1, 2], 2)) #不允许自己和自己的组合
[(1, 2)]</code></pre>
<p>所以针对新问题,解法如下:</p>
<pre><code>bills = [50, 20, 10, 5, 1]
make_100 = []
for n in range(1, 101):
for combination in it.combinations_with_replacement(bills, n):
if sum(combination) == 100:
makes_100.append(combination)</code></pre>
<p>最后的结果我们不需要去重,因为这个方法不会产生重复组合:</p>
<pre><code>>>> len(makes_100)
343</code></pre>
<p>如果你亲自运行一下,可能会注意到输出需要一段时间。那是因为它必须处理96,560,645种组合!这里我们就在执行暴力求解</p>
<p>另一个“暴力” 的itertools函数是permutations(),它接受单个iterable并产生其元素的所有可能的排列(重新排列):</p>
<pre><code>>>> list(it.permutations(['a', 'b', 'c']))
[('a', 'b', 'c'), ('a', 'c', 'b'), ('b', 'a', 'c'),
('b', 'c', 'a'), ('c', 'a', 'b'), ('c', 'b', 'a')]</code></pre>
<p>任何三个元素的可迭代对象(比如list)将有六个排列,并且较长迭代的对象排列数量增长得非常快。实际上,长度为n的可迭代对象有n!排列:</p>
<p><img src="/img/bVboDhU?w=1079&h=959" alt="clipboard.png" title="clipboard.png"></p>
<p>只有少数输入产生大量结果的现象称为组合爆炸,在使用combination(),combinations_with_replacement()和permutations()时我们需要牢记这一点。</p>
<p>说实话,通常最好避免暴力算法,但有时我们可能必须使用(比如算法的正确性至关重要,或者必须考虑每个可能的结果)</p>
<h2>小结</h2>
<p>由于篇幅有限,我先分享到这里,这篇文章我们主要深入理解了以下函数的基本原理:</p>
<ul>
<li>map()</li>
<li>zip()</li>
<li>itertools.combinations</li>
<li>itertools.combinations_with_replacement</li>
<li>itertools.permutations</li>
</ul>
<p>在下一篇文章我会先对最后三个进行总结,然后继续和大家分享itertools里面各种神奇的东西</p>
Python 进阶之路 (八) 最用心的推导式详解 (附简单实战及源码)
https://segmentfault.com/a/1190000018183488
2019-02-17T23:57:08+08:00
2019-02-17T23:57:08+08:00
alpha94511
https://segmentfault.com/u/alpha94511
12
<h2>什么是推导式</h2>
<p>大家好,今天为大家带来问我最喜欢的Python推导式使用指南,让我们先来看看定义~</p>
<p>推导式(comprehensions)是Python的一种独有特性,推导式是可以从一个数据序列构建另一个新的数据序列的结构体。一般有三种使用最多的推导式:</p>
<ul>
<li>列表推导式(list comprehensions)</li>
<li>字典推导式(dict comprehensions)</li>
<li>集合推导式(set comprehensions)</li>
</ul>
<p>使用推导式可以简化代码的同时提高效率,在我的个人使用场景中,用的最多的还是列表推导式,接下来我会一一介绍这三种常见的推导式,最后通过一个简单实战发现推导式的高效之处</p>
<h3>列表推导式(list comprehensions)</h3>
<blockquote>模板</blockquote>
<p>首先,让我们看看使用列表推导式的基础模板:</p>
<ul><li><strong><em>[ expression for item in list if conditional ]</em></strong></li></ul>
<p>简单来说,遍历一个list,得到每一个元素item,我们相对item进行哪些操作,写在expression里就可以,如果对筛选有什么条件,可以放在if后面</p>
<p>下面可以通过大量实例帮助大家理解</p>
<blockquote>使用实例</blockquote>
<p>先看第一个小栗子,在这里我们用for循环常规遍历一个字符串‘human’,把每一字母作为元素放在一个叫h_letters的数组里面:</p>
<pre><code>h_letters = []
for letter in 'human':
h_letters.append(letter)
print(h_letters)
Out:['h', 'u', 'm', 'a', 'n']
</code></pre>
<p>如果我们根据列表推导式的定义模板,可以简化如下:</p>
<pre><code>h_letters = [ letter for letter in 'human' ]
print( h_letters)
Out: ['h', 'u', 'm', 'a', 'n']</code></pre>
<p>这样的话便捷了很多,看上去也很容易理解,这里我们在expression部分什么都没有写,只是提出了每个元素而已,运行时的python执行方式如下:</p>
<p><img src="/img/bVbosmq?w=404&h=133" alt="clipboard.png" title="clipboard.png"></p>
<p>我们可以在expression的部分进行很多操作,比如:</p>
<pre><code>h_letters = [ letter.upper() for letter in 'human' ]
print( h_letters)
Out: ['H', 'U', 'M', 'A', 'N']</code></pre>
<p>这样我们可以很容易的实现字母的大小写转化</p>
<p>同样的,我们可以在if后面写出筛选条件,比如这里,我们想要提出从-20 ~ 20中所有能被3整除的正数:</p>
<pre><code>result = [num for num in range(-20,20)
if num %3==0 and num > 0]
print(result) #多个条件可以用and连接
Out: [3, 6, 9, 12, 15, 18]
</code></pre>
<p>列表推导式的实际应用场景十分广泛,它和lambda不同,是真正好理解,提高效率的python特性之一,这里相信聪明的你已经想到了更多用法!</p>
<h3>字典推导式(dict comprehensions)</h3>
<blockquote>模板</blockquote>
<p>让我们看先来看使用字典推导式的基础模板:</p>
<ul><li><strong><em>{ key:value for key,value in existing_data_structure } </em></strong></li></ul>
<p>这里和list有所不同,因位dict里面有两个关键的属性,key 和 value,但大同小异,我们现在的expression部分可以同时对 key 和 value 进行操作</p>
<p>下面来看最常见的应用</p>
<blockquote>使用实例</blockquote>
<p>首先最实用的功能莫过于可以实现一个dict的key,value值互换:</p>
<pre><code>person = {'name':'xiaobai','city':'paris'}
person_reverse = {v:k for k,v in person.items()} #简单互换key和value的值即可
print(person_reverse)
Out: {'xiaobai': 'name', 'paris': 'city'}
</code></pre>
<p>这里就非常方便了用字典推导式,不然的话如果用for循环,会稍微麻烦一些。<br>让我们再看下一个很巧的例子:</p>
<pre><code>nums = {'a':10,'b':20,'A':5,'B':3,'d':4}
num_frequency = {k.lower():nums.get(k.lower(),0) + nums.get(k.upper(),0)
for k in nums.keys() }
print(num_frequency)
Out: {'a': 15, 'b': 23, 'd': 4}
</code></pre>
<p>这里使用的就比较灵活,我们有一个数据,key是字母的大小写混在一起,我们想统计同一个key(大小写都包括)所对应的数字出现总和,所以在新建的num_frequency 字典用使用了推导式,这里我们遍历的是dict.keys()配合dict.get()方法,当然,如果仅仅是为了实现这个功能,我们有更好的办法,这里只是为了介绍推导式</p>
<p>再比如下面的例子:</p>
<pre><code>fruit = ['apple','banana','organge','mango','peach']
fruit_len = {f:len(f) for f in fruit}
print(fruit_len)
Out:{'apple': 5, 'banana': 6, 'organge': 7, 'mango': 5, 'peach': 5}
</code></pre>
<p>我们有一个fruit的list,现在想要得到每一种水果的单词长度,就可以通过图中所示的方法实现,非常容易</p>
<p>最后再来看一个字典推导式配合枚举(enumerate)的例子:</p>
<pre><code>fruit = ['apple','organge','banana','mango','peach']
fruit_positon = {v:i for i,v in enumerate(fruit)}
print(fruit_positon)
Out: {'apple': 0, 'organge': 1, 'banana': 2, 'mango': 3, 'peach': 4}
</code></pre>
<p>还是用刚才的list,这次我们得到的key是fruit的每个元素,value则是该元素在fruit所在的index</p>
<h3>集合推导式(Set comprehensions)</h3>
<blockquote>模板</blockquote>
<p>让我们看先来看使用集合推导式的基础模板:</p>
<ul><li><strong><em>{ expression for item in Sequence if conditional }</em></strong></li></ul>
<p>其实集合推导式和list的推导式很像,但是既然是集合,肯定会配合利用Set的特有属性来实现我们的目的,如果你还对Set这种数据结构不够了解,可以参考我之前的文章:<a href="https://segmentfault.com/a/1190000018109634?_ea=7068836">Python 进阶之路 (四) 先立Flag, 社区最全的Set用法集锦</a></p>
<p>下面来看最常见的应用</p>
<blockquote>使用实例</blockquote>
<p>首先,我们来看一个根据Set值唯一的特性的例子,我们有一个list叫names,用来存储名字,其中的数据很不规范,有大写,小写,还有重复的,我们想要去重并把名字的格式统一为首字母大写,实现方法便是用Set推导式:</p>
<pre><code>names = [ 'Bob', 'JOHN', 'alice', 'bob', 'ALICE', 'James', 'Bob','JAMES','jAMeS' ]
names_standard = { n[0].upper()+n[1:].lower() for n in names}
print(names_standard)
Out: {'John', 'Bob', 'James', 'Alice'}
</code></pre>
<p>这里就不再举很多的其他例子了,因为使用的方式多种多样,剩下的就靠广大人民群众的智慧自行开发即可!</p>
<h3>简单实战</h3>
<p>现在让我们来看一个比较综合的例子!我们现在手里有一个英文字典的dictionary.txt文件,包含从A~Z的单词</p>
<p>具体需求:我们想要找到长度大于5的正反拼写都具有实际含义的单词</p>
<p>我们现在会通过各种推导式来实现这个目标,我会在文章最后把txt文件及Python文件下载链接附上,这样大家如果先要练习可以自行下载</p>
<p>首先,我们的初始目录结构如下:</p>
<p><img src="/img/bVbosvd?w=377&h=269" alt="clipboard.png" title="clipboard.png"></p>
<p>这里我新建了一个test文件夹,把dictionary.txt 文件和python文件放在一起方便读取,开始之前,先大概看下txt文件长什么样子:</p>
<p><img src="/img/bVbosvt?w=681&h=775" alt="clipboard.png" title="clipboard.png"></p>
<blockquote><strong>第一步:读取dictionary.txt中的单词,选出长度大于5的</strong></blockquote>
<pre><code>
with open('dictionary.txt') as dictionary_file:
words = (line.rstrip() for line in dictionary_file)
words_over_five_letters = [w for w in words if len(w)>5 ]
</code></pre>
<p>这里通过列表推导式words_over_five_letters 用来存储所有长度大于5的单词</p>
<blockquote><strong>第二步:将上一步选出的单词全部以倒序的方式存储在一个集合里</strong></blockquote>
<pre><code class="python">reversed_words ={
word[::-1]
for word in words_over_five_letters
}</code></pre>
<p>通过set推导式来实现</p>
<blockquote><strong>第三步:通过 if 条件筛选得出结果</strong></blockquote>
<pre><code class="python">reversible_words = [
word
for word in words_over_five_letters
if word in reversed_words
]
for word in reversible_words[0:20]:
print(word)
Out:
abrood
agenes
amaroid
amunam
animal
animes
bruted
darter
decart
decurt
deedeed
deflow
degami
degener
degged
deified
deifier
deliver
denier</code></pre>
<p>这里最后共有203个结果,我们只看了前20个,验证方法就是只要长度大于5的单词同时存在于reversed_words和words_over_five_letters即可</p>
<p>完整代码如下:</p>
<pre><code class="python">
with open('dictionary.txt') as dictionary_file:
words = (line.rstrip() for line in dictionary_file)
words_over_five_letters = [w for w in words if len(w)>5 ]
reversed_words ={
word[::-1]
for word in words_over_five_letters
}
reversible_words = [
word
for word in words_over_five_letters
if word in reversed_words
]
for word in reversible_words[0:20]:
print(word)</code></pre>
<blockquote><strong>资料下载</strong></blockquote>
<ul>
<li><a href="https://link.segmentfault.com/?enc=g%2BDmyMoC95CO1Og1cjnxlw%3D%3D.aMnRc76%2B%2BEd5pjN4Hfhjj%2Fj7%2BI0a5B3tl7u8TzDx%2FPS1RntcfxjFSGLxvGQservH" rel="nofollow">dictionary.txt</a></li>
<li><a href="https://link.segmentfault.com/?enc=hguATZnwMGcznI0WGb4%2Fxw%3D%3D.0trLoMLxdwSXkiWi%2FklmHQe4o6U%2FG8baDxhbcoTPwEEqX1SPTx6TtS9nywUNCEBv" rel="nofollow">dictionary.py</a></li>
</ul>
<h2>总结</h2>
<p>这次为大家总结了python里面常见的三种推导式相关用法以及最后的小实战环节,希望大家喜欢,双击666点个赞吧!!</p>
Python 进阶之路 (七) 隐藏的神奇宝藏:探秘Collections
https://segmentfault.com/a/1190000018145641
2019-02-14T06:41:06+08:00
2019-02-14T06:41:06+08:00
alpha94511
https://segmentfault.com/u/alpha94511
10
<h2>神奇的collections</h2>
<p>大家好,今天想和大家分享一个Python里面非常棒的模快:<strong><em>Collections</em></strong></p>
<p>该模块实现了专门的容器数据类型,为Python的通用内置容器提供了替代方案,如果对源码感兴趣的朋友们可以在 <strong><em>Lib/collections/__init__.py</em></strong> 路径下找到</p>
<p>基于我目前的学习经验,以下几种类型用的很多:</p>
<ul>
<li>
<strong>defaultdict</strong> (dict子类调用工厂函数来提供缺失值)</li>
<li>
<strong>counter</strong> (用于计算可哈希对象的dict子类)</li>
<li>
<strong>deque</strong> (类似于列表的容器,可以从两端操作)</li>
<li>
<strong>namedtuple</strong> (用于创建具有命名字段的tuple子类的工厂函数)</li>
<li>
<strong>OrderedDict</strong> (记录输入顺序的dict)</li>
</ul>
<p>好啦,看到什么工厂函数,可哈希对象,容器这些词汇不要慌,我第一次看是懵逼并直接跳过的,然而后来发现根本不需要理解,如果大家感兴趣可以自己去查询,这里还是老样子,通过大量实例来一个个讲解!</p>
<h3>defaultdict</h3>
<blockquote><strong>基础概念</strong></blockquote>
<p>“defaultdict”是在名为“collections”的模块中定义的容器。它需要一个函数(默认工厂)作为其参数。默认情况下设置为“int”,即0.如果键不存在则为defaultdict,并返回并显示默认值。</p>
<p>我用人话解释一下: <strong>其实就是一个查不到key值时不会报错的dict</strong></p>
<blockquote><strong>应用实例</strong></blockquote>
<p>首先我们来看一个用正常dict的例子,如果我们创建了一个叫person的字典,里面存储的key值为name,age,如果这时候尝试调用person['city'],会抛出KeyError错误,因为没有city这个键值:</p>
<pre><code>person = {'name':'xiaobai','age':18}
print ("The value of key 'name' is : ",person['name'])
print ("The value of key 'city' is : ",person['city'])
Out: The value of key 'name' is : xiaobai
Traceback (most recent call last):
File "C:\Users\E560\Desktop\test.py", line 17, in <module>
print ("The value of key 'city' is : ",person['city'])
KeyError: 'city'
</code></pre>
<p>现在如果我们用defaultdict再试试呢?</p>
<pre><code>from collections import defaultdict
person = defaultdict(lambda : 'Key Not found') # 初始默认所有key对应的value均为‘Key Not Found’
person['name'] = 'xiaobai'
person['age'] = 18
print ("The value of key 'name' is : ",person['name'])
print ("The value of key 'adress' is : ",person['city'])
Out:The value of key 'name' is : xiaobai
The value of key 'adress' is : Key Not found</code></pre>
<p>大家可以发现,这次没有问题了,其实最根本的原因在于当我们创建defaultdict时,首先传递的参数是所有key的默认value值,之后我们添加name,age进去的时候才会有所改变,当我们最终查询时,如果key存在,那就输出对应的value值,如果不存在,就会输出我们事先规定好的值‘Key Not Found’</p>
<p>除此之外外,我们还可以利用defaultdict创建时,传递参数为所有key默认value值这一特性,实现一些其他的功能,比如:</p>
<pre><code>from collections import defaultdict
d = defaultdict(list)
d['person'].append("xiaobai")
d['city'].append("paris")
d['person'].append("student")
for i in d.items():
print(i)
Out: ('person', ['xiaobai', 'student'])
('city', ['paris'])
</code></pre>
<p>一个道理,我们默认所有key对应的是一个list,自然就可以在赋值时使用list的append方法了。再比如下面这个例子:</p>
<pre><code>from collections import defaultdict
food = (
('jack', 'milk'),
('Ann', 'fruits'),
('Arham', 'ham'),
('Ann', 'soda'),
('jack', 'dumplings'),
('Ahmed', 'fried chicken'),
)
favourite_food = defaultdict(list)
for n, f in food:
favourite_food[n].append(f)
print(favourite_food)
Out:defaultdict(<class 'list'>, {'jack': ['milk', 'dumplings'], 'Ann': ['fruits', 'soda'], 'Arham': ['ham'], 'Ahmed': ['fried chicken']})</code></pre>
<p>道理和上面差不多,这里大家可以自己拓展,展开想象,相信可能在某个时刻可以用的上defaultdict这个容器</p>
<h3>counter</h3>
<blockquote><strong>基础概念</strong></blockquote>
<p>Counter是dict的子类。因此,它是一个无序集合,其中元素及其各自的计数存储为字典。这相当于其他语言的bag或multiset。</p>
<p>我的理解就是一个计数器,<strong>返回一个字典,key就是出现的元素,value就是该元素出现的次数</strong></p>
<blockquote><strong>应用实例</strong></blockquote>
<p>计数器没啥可说的,还能干啥,计数呗!</p>
<pre><code>from collections import Counter
count_list = Counter(['B','B','A','B','C','A','B','B','A','C']) #计数list
print (count_list)
count_tuple = Counter((2,2,2,3,1,3,1,1,1)) #计数tuple
print(count_tuple)
Out:Counter({'B': 5, 'A': 3, 'C': 2})
Counter({1: 4, 2: 3, 3: 2})</code></pre>
<p><strong>Counter一般不会用于dict和set的计数,因为dict的key是唯一的,而set本身就不能有重复元素</strong></p>
<p>现在我们也可以直接把在defaultdict例子中用过food元组拿来计数:</p>
<pre><code>from collections import Counter
food = (
('jack', 'milk'),
('Ann', 'fruits'),
('Arham', 'ham'),
('Ann', 'soda'),
('jack', 'dumplings'),
('Ahmed', 'fried chicken'),
)
favourite_food_count = Counter(n for n,f in food) #统计name出现的次数
print(favourite_food_count)
Out: Counter({'jack': 2, 'Ann': 2, 'Arham': 1, 'Ahmed': 1})
</code></pre>
<h3>deque</h3>
<blockquote><strong>基础概念</strong></blockquote>
<p>在我们需要在容器两端的更快的添加和移除元素的情况下,可以使用deque.<br>我的个人理解是deque就是一个可以两头操作的容器,类似list但比列表速度更快</p>
<blockquote><strong>应用实例</strong></blockquote>
<p>deque的方法有很多,很多操作和list类似,也支持切片</p>
<pre><code>from collections import deque
d = deque()
d.append(1)
d.append(2)
d.append(3)
print(len(d))
print(d[0])
print(d[-1])
Out: 3
1
3</code></pre>
<p>deque最大的特点在于我们可以从两端操作:</p>
<pre><code>d = deque([i for i in range(5)])
print(len(d))
# Output: 5
d.popleft() # 删除并返回最左端的元素
# Output: 0
d.pop() # 删除并返回最右端的元素
# Output: 4
print(d)
# Output: deque([1, 2, 3])
d.append(100) # 从最右端添加元素
d.appendleft(-100) # 从最左端添加元素
print(d)
# Output: deque([-100, 1, 2, 3, 100])
</code></pre>
<p>除了这些deque的方法实在太多了,比如我再举几个常用的例子,首先我们定义一个deque时可以规定它的最大长度,deque和list一样也支持extend方法,方便列表拼接,但是deque提供双向操作:</p>
<pre><code>from collections import deque
d = deque([1,2,3,4,5], maxlen=9) #设置总长度不变
d.extendleft([0]) # 从左端添加一个list
d.extend([6,7,8]) # 从右端拓展一个list
print(d)
Out:deque([0, 1, 2, 3, 4, 5, 6, 7, 8], maxlen=9)
</code></pre>
<p>现在d已经有9个元素了,而我们规定的maxlen=9,这个时候如果我们从左边添加元素,会自动移除最右边的元素,反之也是一样:</p>
<pre><code>d.append(100)
print(d)
d.appendleft(-100)
print(d)
Out: deque([1, 2, 3, 4, 5, 6, 7, 8, 100], maxlen=9)
deque([-100, 1, 2, 3, 4, 5, 6, 7, 8], maxlen=9)</code></pre>
<p>deque还有很多其他的用法,大家根据各自的需要去自己寻宝吧!</p>
<h3>namedtuple</h3>
<blockquote><strong>基础概念</strong></blockquote>
<p>名称元组。大家一看名字就会感觉和tuple元组有关,没错,我认为它是元组的强化版<br>namedtuple可以将元组转换为方便的容器。使用namedtuple,我们不必使用整数索引来访问元组的成员。</p>
<p>我觉得可以把namedtuple 视为 <strong><em>不可变的</em></strong> 字典</p>
<blockquote>
<strong>应用</strong><strong>实例</strong>
</blockquote>
<p>首先,让我们先回顾一下普通元组是如何访问成员的:</p>
<pre><code>person = ('xiaobai', 18)
print(person[0])
out:xiaobai</code></pre>
<p>现在我们看看namedtuple(名称元组)的强大之处:</p>
<pre><code>from collections import namedtuple
Person = namedtuple('Person', 'name age city') # 类似于定义class
xiaobai = Person(name="xiaobai", age=18, city="paris") # 类似于新建对象
print(xiaobai)
Out:Person(name='xiaobai', age=18, city='paris')
</code></pre>
<p>我们创建namedtuple时非常像定义一个class,这里Person好比是类名,第二个参数就是namedtuple的值的名字了,我感觉很像class里的属性,不过这里不用加逗号分离,下面让我们看看如何访问namedtuple的成员:</p>
<pre><code>print(xiaobai.name)
print(xiaobai.age)
print(xiaobai.city)
out:xiaobai
18
paris</code></pre>
<p><strong><em>"爽啊,爽死了",郭德纲看到这里不禁赞叹</em></strong></p>
<p>这种无限接近class调用属性的方式还是非常不错的,在一些实际场景很有用。<br>最后还有一点千万不要忘了,我们不能修改namedtuple里的值:</p>
<pre><code>xiaobai.name = 'laobai'
Out:Traceback (most recent call last):
File "C:\Users\E560\Desktop\test.py", line 5, in <module>
xiaobai.name = 'laobai'
AttributeError: can't set attribute
</code></pre>
<h3>OrderedDict</h3>
<blockquote><strong>基础概念</strong></blockquote>
<p>“OrderedDict” 本身就是一个dict,但是它的特别之处在于会记录插入dict的key和value的顺序</p>
<blockquote><strong>应用实例</strong></blockquote>
<pre><code>from collections import OrderedDict
d = {}
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['d'] = 4
print(d)
Out:{'a': 1, 'c': 3, 'b': 2, 'd': 4}
</code></pre>
<p>大家可以看到,这是一个普通的dict,因为无序,即使我们依次添加了a,b,c,d 四个键并赋予value,但是输出的顺序并不可控。OrderedDict的出现就是为了解决这个问题:</p>
<pre><code>from collections import OrderedDict
d = OrderedDict()
d['a'] = 1
d['b'] = 2
d['c'] = 3
d['d'] = 4
print(d)
Out:OrderedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
</code></pre>
<p>这回输出时好多了,因为会自动记录插入的顺序,同理,如果我们删除一个key, OrderedDict的顺序不会发生变化:</p>
<pre><code>from collections import OrderedDict
print("Before deleting:\n")
od = OrderedDict()
od['a'] = 1
od['b'] = 2
od['c'] = 3
od['d'] = 4
for key, value in od.items():
print(key, value)
print("\nAfter deleting:\n")
od.pop('c')
for key, value in od.items():
print(key, value)
print("\nAfter re-inserting:\n")
od['c'] = 3
for key, value in od.items():
print(key, value)
Out:Before deleting:
('a', 1)
('b', 2)
('c', 3)
('d', 4)
After deleting:
('a', 1)
('b', 2)
('d', 4)
After re-inserting:
('a', 1)
('b', 2)
('d', 4)
('c', 3)</code></pre>
<h2>总结</h2>
<p>今天为大家简单介绍了collections的一些基础容器类型,包括:</p>
<ul>
<li>
<strong>defaultdict</strong> 不会报错的dict</li>
<li>
<strong>counter</strong> 计数器</li>
<li>
<strong>deque</strong> 双向操作list</li>
<li>
<strong>namedtuple</strong> 名称元组</li>
</ul>
<p>我觉得把它们叫做宝藏感觉还是不过分的,因为这些容器在真实使用场景中非常有用,而且我发现很多教程不会提到,因此衷心希望可以帮到大家,如果我哪里介绍有错误或者遗漏,希望大家留言指出,让我们一起进步!</p>
Python 进阶之路 (六) 九浅一深 lambda,陈独秀你给我坐下!
https://segmentfault.com/a/1190000018121906
2019-02-12T06:35:40+08:00
2019-02-12T06:35:40+08:00
alpha94511
https://segmentfault.com/u/alpha94511
13
<h2><strong>lambda是什么</strong></h2>
<p>大家好,今天给大家带来的是有关于Python里面的lambda表达式详细解析。lambda在Python里面的用处很广,但说实话,我个人认为有关于lambda的讨论不是如何使用的问题,而是该不该用的问题。接下来还是通过大量实例和大家分享我的学习体验,可能最后你也会得出和我一样的结论。</p>
<p>好啦,首先让我们先搞明白基础定义,lambda到底是什么?</p>
<blockquote><strong>Lambda表达了Python中用于创建匿名函数的特殊语法。我们将lambda语法本身称为lambda表达式,从这里得到的函数称之为lambda函数。</strong></blockquote>
<p>其实总结起来,lambda可以理解为一个小的匿名函数,<strong><em>lambda函数可以使用任意数量的参数,但只能有一个表达式。</em></strong>估计有JavaScript ES6经验的朋友们听上去会很亲切,具体函数表达式如下:</p>
<ul>
<li>模板: lambda argument: manipulate(argument)</li>
<li>参数:argument就是这个匿名函数传入的参数,冒号后面是我们对这个参数的操作方法</li>
</ul>
<p>让我们参考上面的定义模板和参数, 直接看一个最简单的例子:</p>
<pre><code>add_one = lambda x:x+1 # 1个参数,执行操作为+1
add_nums = lambda x,y:x+y # 2个参数,执行操作为相加
print(add_one(2)) # 调用add_one
print(add_nums(3,7)) # 调用add_nums
>>> 3
10</code></pre>
<p>相比大家已经发现lambda匿名函数的特点了,就是对于较为简单的功能,无需自己def一个了,单行就可以写下,传参和执行方法一气呵成</p>
<h2><strong>lambda用法详解</strong></h2>
<p>接下来让我们看看lambda的实际应用,就我自己使用lambda的体验来说,从来没有单独用过,lambda一般情况下是和map,filter,reduce这些超棒的内置函数以及dict,list,tuple,set 等数据结构混用,这样才能发挥它的最大效果,如果有朋友还不太熟悉这些内置函数,可以看一下我上一篇文章 <a href="https://segmentfault.com/a/1190000018114755?_ea=6883963">Python 进阶之路 (五) map, filter, reduce, zip 一网打尽</a></p>
<p>好了,闲话少说,下面让我们一个个来看</p>
<h4><strong>lambda + map</strong></h4>
<p>首先出场的是lambda+map的组合,先看下面这个例子:</p>
<pre><code>numbers = [1,2,3,4,5]
add_one = list(map(lambda n:n+1,numbers)) #map(fun,sequence)
print(list(add_one))
print(tuple(add_one))
Out: [2, 3, 4, 5, 6]
(2, 3, 4, 5, 6)</code></pre>
<p>这个是我们上一期的例子,实现一个数组(元组)每个元素+1,让我们回忆一下map的用法<br>map(fun,sequence),fun是传递的方法,sequence是一个可迭代的序列,这里我们的fun就是匿名函数<br>lambda n:n+1,这里非常完美的解释了lambda的设计初衷,因为如果没有lambda,我们的解决方案是这样:</p>
<pre><code>def add(num):
return num+1
numbers = [1,2,3,4,5]
add_one = list(map(add,numbers))
print(add_one)
print(tuple(add_one))</code></pre>
<p>显然易见,这里的add方法有点多余,所以用lambda代替是个好的选择。让我们再看下一个例子,这是我自己备份日志时写的一小段代码,命名不是很规范:</p>
<pre><code>from datetime import datetime as dt
logs = ['serverLog','appLog','paymentLog']
format ='_{}.py'.format(dt.now().strftime('%d-%m-%y'))
result =list(map(lambda x:x+format,logs)) # 利用map+lambda 实现字符串拼接
print(result)
Out:['serverLog_11-02-19.py', 'appLog_11-02-19.py', 'paymentLog_11-02-19.py']
</code></pre>
<p>这里和刚才的加1例子差不多,但是换成了字符串的拼接,然而我这里用lambda并不是很好的解决方案,最后我们会说,现在大家应该对map + lambda 有一些感觉了,让我们再来个和dict字典互动的例子:</p>
<pre><code>person =[{'name':'Lilei',
'city':'beijing'},
{'name':'HanMeiMei',
'city':'shanghai'}]
names=list(map(lambda x:x['name'],person))
print(names)
Out:['Lilei', 'HanMeiMei']
</code></pre>
<p>好了,看到这里对于map+lambda的用法大家已经很清楚了应该~</p>
<h4><strong>lambda + filter</strong></h4>
<p>lambda和filter的组合也很常见,用于特定筛选条件下,现在让我们来看上篇文章filter的例子,就应该很好理解了:</p>
<pre><code>numbers = [0, 1, 2, -3, 5, -8, 13]
# 提取奇数
result = filter(lambda x: x % 2, numbers)
print("Odd Numbers are :",list(result))
# 提取偶数
result = filter(lambda x: x % 2 == 0, numbers)
print("Even Numbers are :",list(result))
#提取正数
result = filter(lambda x: x>0, numbers)
print("Positive Numbers are :",list(result))
Out:Odd Numbers are : [1, -3, 5, 13]
Even Numbers are : [0, 2, -8]
Positive Numbers are : [1, 2, 5, 13]</code></pre>
<p>这里无非就是我们把filter(fun,sequence)里面的fun换成了我们的lambda,只是lambda的函数部分(x%2,x%2==0,x>0)都是可以返回True或者False来判断的,符合fiter的要求,用刚才李雷和韩梅梅的例子也是一个道理:</p>
<pre><code>person =[{'name':'Lilei',
'city':'beijing'},
{'name':'HanMeiMei',
'city':'shanghai'}]
names=list(filter(lambda x:x['name']=='Lilei',person)) # 提取李雷的信息
print(names)
Out:[{'name': 'Lilei', 'city': 'beijing'}]
</code></pre>
<h4><strong>lambda + reduce</strong></h4>
<p>还是让我们看一下上篇文章的例子:</p>
<pre><code>from functools import reduce # Only Python 3
numbers = [1,2,3,4]
result_multiply = reduce((lambda x, y: x * y), numbers)
result_add = reduce((lambda x,y: x+y), numbers)
print(result_multiply)
print(result_add)
Out:24
10
</code></pre>
<p>这个例子用lambda和reduce的配合实现了list求累积和和累积乘法。<br>有意思的是这个例子具有两面性,一方面展示了lambda和reduce如何一起使用,另一方面也引出了接下来我想说的重点:lambda真的值得用吗?到底应该怎么用?</p>
<h2><strong>避免过度使用lambda</strong></h2>
<p>通过上面的例子大家已经看到了lambda的实际应用场景,但是这里我想和大家分享一下我的看法:<strong>我认为lambda的缺点略多于优点,应该避免过度使用lambda</strong></p>
<p>首先,这仅仅是我的个人看法哈,希望大家理解,我为什么这么说呢,首先让我们拿lambda方法和常规def做个对比,我发现lambda和def的主要不同点如下:</p>
<ul>
<li>可以立即传递(无需变量)</li>
<li>只需一行代码,简洁(未必高效)</li>
<li>可以会自动返回,无需return</li>
<li>lambda函数没有函数名称</li>
</ul>
<p>有关优点大家都可以看到,我主要想说一下它的缺点,首先,<strong>从真正需求出发,我们在大多数时候是不需要lambda的,因为总可以找到更好的替代方法</strong>,现在我们一起看一下刚才lambda+reduce 的例子,我们用lambada实现的结果如下:</p>
<pre><code>from functools import reduce # Only Python 3
numbers = [1,2,3,4]
result_multiply = reduce((lambda x, y: x * y), numbers)
result_add = reduce((lambda x,y: x+y), numbers)</code></pre>
<p>这里用lambda并没有实现简单高效的目的,因为我们有现成的sum和mul方法可以用:</p>
<pre><code>
from functools import reduce
from operator import mul
numbers = [1,2,3,4]
result_add = sum(numbers)
result_multiply =reduce(mul,numbers)
print(result_add)
print(result_multiply)
Out: 10
24
</code></pre>
<p>结果是一样的,但是显然用sum和mul的方案更加高效。再举个常见的例子说明,假如我们有一个list存储了各种颜色,现在要求把每个颜色首字母大写,如果用lambda写出是这样:</p>
<pre><code>colors = ['red','purple','green','blue']
result = map(lambda c:c.capitalize(),colors)
print(list(result))
Out:['Red', 'Purple', 'Green', 'Blue']
</code></pre>
<p>看着似乎不错,挺简洁的,但是我们有更好的方法:</p>
<pre><code>
colors = ['red','purple','green','blue']
result = [c.capitalize() for c in colors]
print(result)
Out:['Red', 'Purple', 'Green', 'Blue']
</code></pre>
<p>用sorted还能处理首字母不规范的情况,连排序都省了:</p>
<pre><code>colors = ['Red','purple','Green','blue']
print(sorted(colors,key=str.capitalize))
Out:['blue', 'Green', 'purple', 'Red']
</code></pre>
<p>还有一个主要原因就是: <strong>lambda函数没有函数名称</strong>。所以在代码交接,项目移植的场景中会给团队带来很多困难,多写个函数add_one()没什么坏处,因为大家都很容易理解,知道它是执行+1的功能,但是如果团队里你在自己负责的模块使用了很多lambda,会给其他人理解带来很多麻烦</p>
<h2><strong>适合lambda的场景</strong></h2>
<p>话又说回来,存在即合理,那么真正需要我们使用lambda的是哪些场景呢:</p>
<ol>
<li>你需要的方法是很简单的(+1,字符串拼接等),该函数不值得拥有一个名字</li>
<li>使用lambda表达式,会比我们能想到的函数名称更容易理解</li>
<li>除了lambda,没有任何python提供的函数可以实现目的</li>
<li>团队中所有成员都掌握lambda,大家同意你用</li>
</ol>
<p>还有一种场景非常适用,就是在给其他人制造自己很专业的错觉时,比如:</p>
<blockquote><strong>哎呀,小老弟,听说你学了Python,知道lambda不? 没听过?不行啊,白学了!来来来,让我给你讲讲。。。此处省略1万字</strong></blockquote>
<h2><strong>总结</strong></h2>
<p>今天为大家九浅一深地讲解了lambda的用法和使用场景,所谓九浅一深,就是90%情况下用于创建简单的匿名函数,10%的情况稍微复杂(我这个借口找的太好了)</p>
<p>总而言之就是,任何事情都具有两面性,我们在使用lambda之前应该先停下来,问问自己是不是真的需要它。</p>
<p>当然,如果需要和别人忽悠的时候都是正反一张嘴,lambda是好是坏全看我们自己怎么说,吹牛时请遵守如下原则,屡试不爽:</p>
<blockquote>
<strong>如果你说一个女大学生晚上卖淫就是可耻,但如果改成一个妓女利用业余时间努力学习就励志多了</strong>!</blockquote>
<p>lambda也是如此</p>
Python 进阶之路 (五) map, filter, reduce, zip 一网打尽
https://segmentfault.com/a/1190000018114755
2019-02-11T03:03:12+08:00
2019-02-11T03:03:12+08:00
alpha94511
https://segmentfault.com/u/alpha94511
14
<h2>简洁的内置函数</h2>
<p>大家好,我又回来了,今天我想和大家分享的是Python非常重要的几个内置函数:map,filter,reduce, zip。<br>它们都是处理序列的便捷函数。这很大程度上归功于函数式编程的世界。我们可以利用它们把一些小函数应用于一个序列的所有元素。从而节省编写显式循环的时间。</p>
<p>另外,这些中的每一个都是纯函数,有返回值。因此我们可以容易地将函数的返回结果用表达式来表示。</p>
<p>好了,又到了大白话时间,为什么用它们,就是可以简化我们的代码,更简洁高效的执行一些需要用到循环迭代为主的任务,接下来让我们一个个来看</p>
<h3><strong><em>map()</em></strong></h3>
<blockquote>函数构造</blockquote>
<p>map()函数的主要作用是可以把一个方法依次执行在一个可迭代的序列上,比如List等,具体的信息如下:</p>
<ul>
<li>基础语法:map(fun, iterable)</li>
<li>参数:fun是map传递给定可迭代序列的每个元素的函数。iterable是一个可以迭代的序列,序列中的每一个元素都可以执行fun</li>
<li>返回值:map object</li>
</ul>
<p>好了,大白话就是利用map我们可以把一个函数fun 执行到序列iter的每一个元素上,用例子非常好理解~</p>
<blockquote>基础用法:</blockquote>
<p>下面先让我们看一个小例子,假设现在我们有一个List,包含1~5五个数字,我们想要让每一个数+1,如果不知道map这个函数之前,我们的解决方案是这样的:</p>
<pre><code>numbers = [1, 2, 3, 4, 5]
for i in range(0,len(numbers)): #对每个元素加1
numbers[i]+=1
print(numbers)
Out:[2, 3, 4, 5, 6]
</code></pre>
<p>或者是这样的:</p>
<pre><code>numbers = [1, 2, 3, 4, 5]
result = []
for n in numbers:
result.append(n+1)
print(result)
Out:[2, 3, 4, 5, 6]
</code></pre>
<p>但是显然,无论怎么做都会涉及到写循环,这里就是map函数的用武之地了,我们可以用map函数这样实现:</p>
<pre><code>def add_one(n):
return n + 1
numbers = [1, 2, 3, 4, 5]
result = map(add_one, numbers)
print(result)
print(type(result))
print(list(result))
Out:<map object at 0x00000260F508BE80>
<class 'map'>
[2, 3, 4, 5, 6]
</code></pre>
<p>这里想必聪明的你发现了map的好处,在优化精简代码的同时,某种程度上讲实现了方法和循环部分的分离,这里我们可以发现map返回就是map类,我们这里传递的序列是List,最后输出时经过类型转换也是list</p>
<p>在传递序列时只要这个序列是可迭代的就好,不一定非要List,比如我们换一种:</p>
<pre><code>def add_one(n):
return n + 1
numbers = (1, 2, 3, 4, 5) #序列为元组
result = map(add_one, numbers)
print(tuple(result)) #
Out:(2, 3, 4, 5, 6)
</code></pre>
<p>输入的序列为同样可以迭代的元组,输出时我们也选择元组,效果一样的。</p>
<blockquote>更进一步</blockquote>
<p>还用刚才的例子,为了更加简洁,我们可以用lambda函数配合map使用,具体实现如下:</p>
<pre><code>numbers = (1, 2, 3, 4, 5) # 迭代对象为tuple
result = map(lambda x: x + 1, numbers)
print(list(result)) # 输出对象为list
Out:[2, 3, 4, 5, 6]
</code></pre>
<p>更加简洁优雅了对吧!!这个lambad函数我之后会说,今天它不是主角哈哈,先一带而过。<br>让我们重新把目光转移到map上来,除了刚才的用法,还要一种情况也十分常见,让我们看下面的例子:</p>
<pre><code># List of strings
words = ['paris', 'xiaobai','love']
# 把数组中每个元素变为List
test = list(map(list, words))
print(test)
Out: [['p', 'a', 'r', 'i', 's'], ['x', 'i', 'a', 'o', 'b', 'a', 'i'], ['l', 'o', 'v', 'e']]
</code></pre>
<p>words是一个只包含字符串类型元素的list,我们用map可以实现将words的每一个元素全部转化为list类型,这里有一点一定要注意,能实现的前提一定是每个元素都是可以迭代的类型,如果出现了如int类型的元素,就会出错啦:</p>
<pre><code># List of strings
words = [18,'paris', 'xiaobai','love']
# 把数组中每个元素变为List
test = list(map(list, words))
print(test)
Out:TypeError: 'int' object is not iterable
</code></pre>
<p>大家一看错误类型相比立刻就明白啦,所以正确的使用方法一定是类似这种:</p>
<pre><code>nums = [3,"23",-2]
print(list(map(float,nums)))
Out: [3.0, 23.0, -2.0]
</code></pre>
<p>总之就是类型要注意,今天我就抛砖引玉简单介绍一下map,具体的用法大家可以自行开发哈,我也在不断学习中</p>
<h3><strong><em>filter()</em></strong></h3>
<blockquote>函数构造</blockquote>
<p>filter()方法借助于一个函数来过滤给定的序列,该函数测试序列中的每个元素是否为真。</p>
<ul>
<li>基础语法:filter(fun, iterable)</li>
<li>参数:fun测试iterable序列中的每个元素执行结果是否为True,iterable为被过滤的可迭代序列</li>
<li>返回值:可迭代的序列,包含元素对于fun的执行结果都为True</li>
</ul>
<p>简而言之就是filter可以帮助我们根据给出的条件过滤一组数据并返回结果</p>
<blockquote>基础用法:</blockquote>
<p>让我们先看一个例子:</p>
<pre><code># 过滤元音的方法
def fun(variable):
letters = ['a', 'e', 'i', 'o', 'u']
if (variable in letters):
return True
else:
return False
# 给定序列
sequence = ['I', 'l', 'o', 'v', 'e', 'p', 'y','t','h','o','n']
# 根据条件得出结果
filtered = list(filter(fun, sequence))
print(filtered)
Out:['o', 'e', 'o']
</code></pre>
<p>这里我们创建一个可以提取元音字母的方法fun,给定的可迭代序列为list,之后就可以用filter方法很容易的提取出结果啦,再看一个类似例子:</p>
<pre><code># 判断为正数
def positive(num):
if num>0:
return True
else:
return False
#判断偶数
def even(num):
if num % 2==0:
return True
else:
return False
numbers=[1,-3,5,-20,0,9,12]
positive_nums = list(filter(positive, numbers))
print(positive_nums) # 输出正数 list
even_nums = tuple(filter(even,numbers))
print(even_nums) #输出偶数 tuple
Out:[1, 5, 9, 12]
(-20, 0, 12)</code></pre>
<p>看到这里相比大家已经知道filter的基础用法啦, 要先有一个,能返回True或者False的方法,或者表达式作为过滤条件就行啦</p>
<blockquote>更进一步</blockquote>
<p>这里其实和map一样了,基本上最简洁的用法都是和lambda混在一起,比如下面我们想要把刚才的一大串代码压缩一下:</p>
<pre><code>numbers = [0, 1, 2, -3, 5, -8, 13]
# 提取奇数
result = filter(lambda x: x % 2, numbers)
print("Odd Numbers are :",list(result))
# 提取偶数
result = filter(lambda x: x % 2 == 0, numbers)
print("Even Numbers are :",list(result))
#提取正数
result = filter(lambda x: x>0, numbers)
print("Positive Numbers are :",list(result))
Out:Odd Numbers are : [1, -3, 5, 13]
Even Numbers are : [0, 2, -8]
Positive Numbers are : [1, 2, 5, 13]
</code></pre>
<p>" 爽啊!爽死了!" 郭德纲看到后这么评价,lambda我平时用的不多,但是写到这里,我也觉得要好好学习它了,毕竟和其他编程语言相比,可能这中用法才是python提倡的理念之一:高效简洁,</p>
<h3><strong><em>reduce()</em></strong></h3>
<blockquote>函数构造</blockquote>
<p>Reduce是一个非常有用的函数,用于在列表上执行某些计算并返回结果。它将滚动计算应用于列表中的连续值。例如,如果要计算整数列表的累积乘,或者求和等等</p>
<ul>
<li>基础语法:reduce(function, iterable)</li>
<li>参数:fun是连续作用于iterable每一个元素的方法,新的参数为上一次执行的结果,iterable为被过滤的可迭代序列</li>
<li>返回值:最终的function返回结果</li>
</ul>
<p>在Python 2中,reduce()是一个内置函数。但是,在Python 3中,它被移动到functools模块。因此,要使用前我们需要导入,这里我的环境是Python 3.6</p>
<blockquote>基础用法:</blockquote>
<p>先看一个求累加和的小栗子:</p>
<pre><code>from functools import reduce # Python 3
def do_sum(x1, x2):
return x1 + x2
print(reduce(do_sum, [1, 2, 3, 4]))
Out:10</code></pre>
<p>再看一个累积乘法的例子:</p>
<pre><code>from functools import reduce # Python 3
def multiply(x, y):
return x*y
numbers = [1,2,3,4]
print(reduce(multiply, numbers))
Out:24</code></pre>
<blockquote>更进一步:</blockquote>
<p>还是和lambda混搭,更加简洁:</p>
<pre><code>from functools import reduce # Python 3
numbers = [1,2,3,4]
result_multiply = reduce((lambda x, y: x * y), numbers)
result_add = reduce((lambda x,y: x+y), numbers)
print(result_multiply)
print(result_add)
Out:24
10</code></pre>
<h3><strong><em>zip()</em></strong></h3>
<blockquote>函数构造</blockquote>
<p>zip()的目的是映射多个容器的相似索引,以便它们可以仅作为单个实体使用。</p>
<ul>
<li>基础语法:zip(*iterators)</li>
<li>参数:iterators为可迭代的对象,例如list,string</li>
<li>返回值:返回单个迭代器对象,具有来自所有容器的映射值</li>
</ul>
<blockquote>基础用法:</blockquote>
<p>其实之前我们在讲dict的创建方法时提到过它,这里从新回顾一下:</p>
<pre><code>
keys = ['name','age']
values = ['xiaobai',18]
my_dict = dict(zip(keys,values))
print(my_dict)
Out:{'name': 'xiaobai', 'age': 18}
</code></pre>
<p>zip可以支持多个对象,比如下面的例子</p>
<pre><code>name = [ "xiaobai", "john", "mike", "alpha" ]
age = [ 4, 1, 3, 2 ]
marks = [ 40, 50, 60, 70 ]
# using zip() to map values
mapped = list(zip(name, age, marks))
print ("The zipped result is : "mapped)
Out:The zipped result is : [('xiaobai', 4, 40), ('john', 1, 50), ('mike', 3, 60), ('alpha', 2, 70)]
</code></pre>
<p>这里我们可以很容易的的把name,age,marks这三个list里面相同index的值映射打包在一起</p>
<blockquote>更进一步:</blockquote>
<p>通过上面的例子,我们发现可以很容易的以类似1对1的形式把不同对象的同一索引位置的值打包起来,那如果是解包呢?也是类似的,就是多了一个 * 而已</p>
<pre><code>names, ages, marks = zip(*mapped)
print ("The name list is : ",names)
print ("The age list is : ",ages)
print ("The marks list is : ",marks)
Out: The name list is : ('xiaobai', 'john', 'mike', 'alpha')
The age list is : (4, 1, 3, 2)
The marks list is : (40, 50, 60, 70)</code></pre>
<h2>总结</h2>
<p>今天主要为大家介绍了map,filter,reduce,zip四个高效的python内置函数的用法,我也是刚刚接触,了解不够深入,如果介绍的有错误或者歧义还请大家多多谅解和包容,如果有大神可以进一步补充说明一定要写个评论呀,让我们一起进步。</p>
<p>最后为大家讲个悲伤的故事:</p>
<p><img src="/img/bVboaDC?w=500&h=430" alt="clipboard.png" title="clipboard.png"></p>
Python 进阶之路 (四) 先立Flag, 社区最全的Set用法集锦
https://segmentfault.com/a/1190000018109634
2019-02-09T07:07:51+08:00
2019-02-09T07:07:51+08:00
alpha94511
https://segmentfault.com/u/alpha94511
39
<h2>Set是什么</h2>
<p>大家好,恰逢初五迎财神,先预祝大家新年财源滚滚!!<br>在上一期详解tuple元组的用法后,今天我们来看Python里面最后一种常见的数据类型:集合(Set)</p>
<p>与dict类似,set也是一组key的集合,但不存储value。由于key不能重复,所以,在set中,没有重复的key。创建一个set,需要提供一个list作为输入集集合,重复元素在set中会被自动被过滤,通过add(key)方法往set中添加元素,重复添加不会有效果。如果现在你发现我讲的很模糊请不要着急。稍后会有海量例子为大家详解。</p>
<p>总而言之,Set具有三个显著特点:</p>
<ul>
<li>无序</li>
<li>元素是独一无二的,不允许出现重复的元素</li>
<li>可以修改集合本身,但集合中包含的元素必须是不可变类型</li>
</ul>
<p>现在让我们开启Set奇幻之旅,我希望这篇文章是SegmentFault社区对于Set介绍最全的模范,哈哈!</p>
<h3>定义一个Set</h3>
<p>我们有两种方式可以创建一个Set,可以使用内置的<strong>set()</strong>方法,或是使用中括号<strong>{}</strong><br>创建模板如下:</p>
<pre><code> x = set(<iter>)
x = {<obj>, <obj>, ..., <obj>}
</code></pre>
<p>现在让我们来看例子~</p>
<blockquote><strong>set()内置方法创建</strong></blockquote>
<pre><code>x = set(['foo', 'bar', 'baz', 'foo', 'qux']) # 传入List
print(x)
y = set(('foo', 'bar', 'baz', 'foo', 'qux')) #传入元组
print(y)
Out: {'qux', 'foo', 'bar', 'baz'} # 注意到无序了吧~
{'bar', 'qux', 'baz', 'foo'}
</code></pre>
<p>这里要注意用set()内置方法创建时一定要传递一个可以迭代的参数,还有从输出结果相信大家已经发现set的第一个特点了:无序</p>
<p>字符串也是可迭代的,因此字符串也可以传递给set()</p>
<pre><code>s = 'quux'
a = set(s)
print(a)
Out: {'u', 'q', 'x'} # 无序,唯一
</code></pre>
<p>这里又体现了set的第二个特点:元素唯一性</p>
<blockquote><strong>{} 方法创建</strong></blockquote>
<pre><code>>>> x = {'foo', 'bar', 'baz', 'foo', 'qux'}
>>> x
{'qux', 'foo', 'bar', 'baz'}</code></pre>
<p>这里考虑到之后例子太多,实在不能每次都打print啦,这种形式大家看的更清楚,这个直接用{}创建很简单,只要传递进元素就行啦</p>
<blockquote><strong>创建空集合</strong></blockquote>
<p>Set可以是空的。但是,请记住Python将空花括号{}解释为空字典,因此定义空集的唯一方法是使用set()函数</p>
<pre><code>>>> x = set()
>>> type(x)
<class 'set'>
>>> x = {}
>>> type(x)
<class 'dict'>
</code></pre>
<p>一个空集合用布尔类型显示为False</p>
<pre><code>>>> x = set()
>>> bool(x)
False
>>> x or 1
1
>>> x and 1
set()</code></pre>
<blockquote><strong>对比小结</strong></blockquote>
<p>对于这两种方法创建Set,本质区别在于以下两点</p>
<ol>
<li>set()的参数是可迭代的。它会生成要放入集合中的所有元素组成的List。</li>
<li>花括号 {} 中的对象完整地放入集合中,即使它们是可迭代的。</li>
</ol>
<blockquote><strong>补充说明</strong></blockquote>
<p>集合中的元素可以是不同类型的对象,不一定非要是同一类型的,可以包含不同类型,比如:</p>
<pre><code>>>> x = {42, 'foo', 3.14159, None}
>>> x
{None, 'foo', 42, 3.14159}</code></pre>
<p>但同时不要忘记set元素必须是不可变的。例如,元组可以包括在集合中:</p>
<pre><code>>>> x = {42, 'foo', (1, 2, 3), 3.14159}
>>> x
{42, 'foo', 3.14159, (1, 2, 3)}</code></pre>
<p>但列表和字典是可变的,因此它们不能成为Set的元素:</p>
<pre><code>>>> a = [1, 2, 3]
>>> {a}
Traceback (most recent call last):
File "<pyshell#70>", line 1, in <module>
{a}
TypeError: unhashable type: 'list'
>>> d = {'a': 1, 'b': 2}
>>> {d}
Traceback (most recent call last):
File "<pyshell#72>", line 1, in <module>
{d}
TypeError: unhashable type: 'dict'</code></pre>
<h3>Set大小以及成员</h3>
<p>len()函数返回集合中元素的数量,而in和not in运算符可用于测试是否为Set中的元素:</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> len(x)
3
>>> 'bar' in x
True
>>> 'qux' in x
False</code></pre>
<h3>Set基本操作</h3>
<blockquote><strong>方法和运算符</strong></blockquote>
<p>许多可用于Python其他数据类型的操作对集合没有意义。例如,无法对集合建立索引或切片。但是,Python在set对象上提供了运算符,这些操作符其实很多和数学里是一模一样的,相信数学好的朋友们对这部分简直不要太熟悉</p>
<p>所以对于Set的操作除了用普通的内置方法,我们也可以使用运算符,比较方便</p>
<blockquote><strong>Union 并集</strong></blockquote>
<ul>
<li>用法:计算两个或更多集合的并集。</li>
<li>方法: x1.union(x2[, x3 ...])</li>
<li>运算符:x1 | x2 [| x3 ...]</li>
</ul>
<p>让我们新建两个Set做测试:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}</code></pre>
<p>现在我们想求x1,x2的并集,如下图所示:</p>
<p><img src="/img/bVbn9iR?w=594&h=393" alt="clipboard.png" title="clipboard.png"></p>
<p>具体实现方法如下,或是用方法,或是用操作符:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.union(x2)
{'foo', 'qux', 'quux', 'baz', 'bar'}
>>> x1 | x2
{'foo', 'qux', 'quux', 'baz', 'bar'}</code></pre>
<p>如果有两个以上的Set也是没有问题的,原理都是一样的:</p>
<pre><code>>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.union(b, c, d)
{1, 2, 3, 4, 5, 6, 7}
>>> a | b | c | d
{1, 2, 3, 4, 5, 6, 7}</code></pre>
<blockquote><strong>Intersection 交集</strong></blockquote>
<ul>
<li>方法: x1.intersection(x2[, x3 ...])</li>
<li>运算符:x1 & x2 [& x3 ...]</li>
<li>用法:计算两个或更多集合的交集。</li>
</ul>
<p>现在还让我们用刚才创建好的两个set,所求部分如下图:</p>
<p><img src="/img/bVbn9iQ?w=643&h=399" alt="clipboard.png" title="clipboard.png"></p>
<pre><code>实现仍然是两种方法:
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.intersection(x2)
{'baz'}
>>> x1 & x2
{'baz'}</code></pre>
<p>多个集合的情况公示和方法依然有效,结果仅包含所有指定集合中都存在的元素。</p>
<pre><code>>>> a = {1, 2, 3, 4}
>>> b = {2, 3, 4, 5}
>>> c = {3, 4, 5, 6}
>>> d = {4, 5, 6, 7}
>>> a.intersection(b, c, d)
{4}
>>> a & b & c & d
{4}</code></pre>
<blockquote><strong>Difference 差集</strong></blockquote>
<ul>
<li>方法: x1.difference(x2[, x3 ...])</li>
<li>运算符:x1 - x2 [- x3 ...]</li>
<li>用法:计算两个或更多集合的差集。大白话说就是x1去除x1和x2的共有元素</li>
</ul>
<p>下图所示为x1.difference(x2)的目标结果:<br><img src="/img/bVbn9iS?w=625&h=396" alt="clipboard.png" title="clipboard.png"></p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.difference(x2)
{'foo', 'bar'}
>>> x1 - x2
{'foo', 'bar'}
</code></pre>
<p>还是老样子,适用于2个及以上的集合:</p>
<pre><code>>>> a = {1, 2, 3, 30, 300}
>>> b = {10, 20, 30, 40}
>>> c = {100, 200, 300, 400}
>>> a.difference(b, c)
{1, 2, 3}
>>> a - b - c
{1, 2, 3}</code></pre>
<p>指定多个集合时,操作从左到右执行。在上面的示例中,首先计算a - b,得到{1,2,3,300}。然后从该集合中减去c,留下{1,2,3},具体流程如下图所示:</p>
<p><img src="/img/bVbn9iT?w=654&h=286" alt="clipboard.png" title="clipboard.png"></p>
<blockquote><strong>Symmetric Difference 对称差集</strong></blockquote>
<ul>
<li>方法: x1.symmetric_difference(x2)</li>
<li>运算符:x1 ^ x2 <sup id="fnref-1"><a href="#fn-1" class="footnote-ref">1</a></sup>
</li>
<li>用法:计算两个或更多集合的差集。大白话说就是x1去除x1和x2的共有元素</li>
</ul>
<p>下图所示为x1.symmetric_difference(x2)的目标结果:</p>
<p><img src="/img/bVbn9iU?w=664&h=390" alt="clipboard.png" title="clipboard.png"></p>
<p>实现方法如下;</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.symmetric_difference(x2)
{'foo', 'qux', 'quux', 'bar'}
>>> x1 ^ x2
{'foo', 'qux', 'quux', 'bar'}</code></pre>
<p>老规矩,支持2个及以上set的连续操作:</p>
<pre><code>>>> a = {1, 2, 3, 4, 5}
>>> b = {10, 2, 3, 4, 50}
>>> c = {1, 50, 100}
>>> a ^ b ^ c
{100, 5, 10}</code></pre>
<p>当指定多个集合时,操作从左到右执行,奇怪的是,<strong><em>虽然 ^ 运算符允许多个集合,但.symmetric_difference()方法不允许</em></strong></p>
<pre><code>>>> a = {1, 2, 3, 4, 5}
>>> b = {10, 2, 3, 4, 50}
>>> c = {1, 50, 100}
>>> a.symmetric_difference(b, c)
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
a.symmetric_difference(b, c)
TypeError: symmetric_difference() takes exactly one argument (2 given)</code></pre>
<blockquote><strong>x1.isdisjoint(x2) 判断是否相交</strong></blockquote>
<ul>
<li>方法: x1.isdisjoint(x2)</li>
<li>用法:确定两个集合是否具有任何共同的元素</li>
</ul>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1.isdisjoint(x2)
False
>>> x2 - {'baz'}
{'quux', 'qux'}
>>> x1.isdisjoint(x2 - {'baz'})
True</code></pre>
<p>从这个栗子可以看出,如果两个Set没有共同元素返回True,如果有返回True,如果返回True同时也意味着<br>他们之间的交集为空集,这个很好理解:</p>
<pre><code>>>> x1 = {1, 3, 5}
>>> x2 = {2, 4, 6}
>>> x1.isdisjoint(x2)
True
>>> x1 & x2
set()</code></pre>
<p><strong><em>注意:目前还没有运算符对应这个方法</em></strong></p>
<blockquote><strong>x1.issubset(x2) 判断x1是否为x2子集</strong></blockquote>
<ul>
<li>方法: x1.issubset(x2)</li>
<li>运算符:x1 <= x2</li>
<li>用法:如果返回True,x1为x2子集,反之返回False</li>
</ul>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issubset({'foo', 'bar', 'baz', 'qux', 'quux'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 <= x2
False</code></pre>
<p>一个集合本身当然是它自己的子集啦:</p>
<pre><code>>>> x = {1, 2, 3, 4, 5}
>>> x.issubset(x)
True
>>> x <= x
True</code></pre>
<blockquote><strong>x1<x2 判断x1是否为x2的真子集</strong></blockquote>
<ul>
<li>运算符:x1<x2</li>
<li>用法:判断x1是否为x2的真子集,如果返回True,x1为x2的真子集,反之返回False</li>
</ul>
<p>首先。。。让我们回顾一下数学知识:真子集与子集类似,除了集合不能相同。如果x1的每个元素都在x2中,并且x1和x2不相等,则集合x1被认为是另一个集合x2的真子集</p>
<p>换个高大上的说法也可以:如果集合A⊆B,存在元素x∈B,且元素x不属于集合A,我们称集合A与集合B有真包含关系,集合A是集合B的真子集(proper subset)。记作A⊊B(或B⊋A),读作“A真包含于B”(或“B真包含A”)</p>
<pre><code>>>> x1 = {'foo', 'bar'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
True
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 < x2
False</code></pre>
<p>虽然Set被认为是其自身的子集,但它本身并不是自己的真子集:</p>
<pre><code>>>> x = {1, 2, 3, 4, 5}
>>> x <= x
True
>>> x < x
False</code></pre>
<p><strong><em>注意:目前还没有方法对应这个运算符</em></strong></p>
<blockquote><strong>x1.issuperset(x2) 判断x1是否为x2的超集</strong></blockquote>
<ul>
<li>方法:x1.issuperset(x2)</li>
<li>运算符:x1 >= x2</li>
<li>用法:判断x1是否为x2的超集,如果是返回True,反之返回False</li>
</ul>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x1.issuperset({'foo', 'bar'})
True
>>> x2 = {'baz', 'qux', 'quux'}
>>> x1 >= x2
False</code></pre>
<p>我们刚才已经看到过了一个Set是它自己本身的子集,这里也是一样的,它同时也是自己的超集</p>
<pre><code>>>> x = {1, 2, 3, 4, 5}
>>> x.issuperset(x)
True
>>> x >= x
True</code></pre>
<blockquote><strong>x1 > x2 判断x1是否为x2的真超集</strong></blockquote>
<ul>
<li>运算符:x1 > x2</li>
<li>用法:判断x1是否为x2的真超集,如果是返回True,反之返回False</li>
</ul>
<p>真超集与超集相同,除了集合不能相同。如果x1包含x2的每个元素,并且x1和x2不相等,则集合x1被认为是另一个集合x2的真超集。</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar'}
>>> x1 > x2
True
>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'bar', 'baz'}
>>> x1 > x2
False</code></pre>
<p>一个集合不是它自己的真超集,和真子集的原理相同</p>
<pre><code>>>> x = {1, 2, 3, 4, 5}
>>> x > x
False</code></pre>
<h3>对Set进行修改</h3>
<p>虽然集合中包含的元素必须是不可变类型,但可以修改集合本身。与上面的操作类似,可以使用多种运算符和方法来更改集合的内容。</p>
<blockquote><strong>x1.update(x2) 通过union修改集合元素 </strong></blockquote>
<ul>
<li>方法:x1.update(x2[, x3 ...])</li>
<li>运算符:x1 |= x2 [| x3 ...]</li>
<li>用法:通过union修改集合</li>
</ul>
<p>x1.update(x2) 和 x1 |= x2 作用是向集合x1中添加x2中所有x1不存在的元素。<br>停下3秒,我仔细读了这句话,觉得我表达的还可以,不知道大家读上去绕不绕,先看例子:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 |= x2
>>> x1
{'qux', 'foo', 'bar', 'baz'}
>>> x1.update(['corge', 'garply'])
>>> x1
{'qux', 'corge', 'garply', 'foo', 'bar', 'baz'}
</code></pre>
<blockquote><strong>x1.intersection(x2) 通过intersection修改集合元素 </strong></blockquote>
<ul>
<li>方法:x1.intersection_update(x2[, x3 ...])</li>
<li>运算符:x1 &= x2 [& x3 ...]</li>
<li>用法:通过intersection修改集合</li>
</ul>
<p>x1.intersection_update(x2) 和 x1 &= x2 会让x1只保留x1和x2的交集部分:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 &= x2
>>> x1
{'foo', 'baz'}
>>> x1.intersection_update(['baz', 'qux'])
>>> x1
{'baz'}</code></pre>
<blockquote><strong>x1.difference_update(x2) 通过difference修改集合元素 </strong></blockquote>
<ul>
<li>方法:x1.difference_update(x2[, x3 ...])</li>
<li>运算符:x1 -= x2 [| x3 ...]</li>
<li>用法:通过difference修改集合</li>
</ul>
<p>x1.difference_update(x2) and x1 -= x2 会让集合x1移除所有在x2出现的属于x1的元素:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>> x1 -= x2
>>> x1
{'bar'}
>>> x1.difference_update(['foo', 'bar', 'qux'])
>>> x1
set()</code></pre>
<blockquote><strong>x1.symmetric_difference_update(x2) 通过对称差集修改集合元素 </strong></blockquote>
<ul>
<li>方法:x1.symmetric_difference_update(x2)</li>
<li>运算符:x1 ^= x2</li>
</ul>
<p>这个我实在用语言解释不清了,看例子容易懂:</p>
<pre><code>>>> x1 = {'foo', 'bar', 'baz'}
>>> x2 = {'foo', 'baz', 'qux'}
>>>
>>> x1 ^= x2
>>> x1
{'bar', 'qux'}
>>>
>>> x1.symmetric_difference_update(['qux', 'corge'])
>>> x1
{'bar', 'corge'}</code></pre>
<blockquote><strong>x.add(<elem> 添加元素</strong></blockquote>
<p>这个就很简单了, 类似List:</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> x.add('qux')
>>> x
{'bar', 'baz', 'foo', 'qux'}</code></pre>
<blockquote><strong>x.remove(<elem>) 删除元素</strong></blockquote>
<p>如果删除的元素不存在会抛出异常</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> x.remove('baz')
>>> x
{'bar', 'foo'}
>>> x.remove('qux')
Traceback (most recent call last):
File "<pyshell#58>", line 1, in <module>
x.remove('qux')
KeyError: 'qux'</code></pre>
<p>这个时候为了避免出现错误可以用discard方法</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> x.discard('baz')
>>> x
{'bar', 'foo'}
>>> x.discard('qux')
>>> x
{'bar', 'foo'}</code></pre>
<p>利用pop删除随机元素并返回:</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> x.pop()
'bar'
>>> x
{'baz', 'foo'}
>>> x.pop()
'baz'
>>> x
{'foo'}
>>> x.pop()
'foo'
>>> x
set()</code></pre>
<p>利用clear可以清空一个集合:</p>
<pre><code>>>> x = {'foo', 'bar', 'baz'}
>>> x
{'foo', 'bar', 'baz'}
>>>
>>> x.clear()
>>> x
set()</code></pre>
<h2>Frozen Sets</h2>
<blockquote><strong>Frozen Sets是什么东西</strong></blockquote>
<p>Python提供了另一种称为冻结集合Frozen Sets的内置类型,它在所有方面都与集合完全相同,只不过Frozen Sets是不可变的。我们可以对冻结集执行非修改操作,比如:</p>
<pre><code>>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x
frozenset({'foo', 'baz', 'bar'})
>>> len(x)
3
>>> x & {'baz', 'qux', 'quux'}
frozenset({'baz'})</code></pre>
<p>如果胆敢尝试修改Frozen Sets:</p>
<pre><code>>>> x = frozenset(['foo', 'bar', 'baz'])
>>> x.add('qux')
Traceback (most recent call last):
File "<pyshell#127>", line 1, in <module>
x.add('qux')
AttributeError: 'frozenset' object has no attribute 'add'
>>> x.pop()
Traceback (most recent call last):
File "<pyshell#129>", line 1, in <module>
x.pop()
AttributeError: 'frozenset' object has no attribute 'pop'
>>> x.clear()
Traceback (most recent call last):
File "<pyshell#131>", line 1, in <module>
x.clear()
AttributeError: 'frozenset' object has no attribute 'clear'
>>> x
frozenset({'foo', 'bar', 'baz'})</code></pre>
<blockquote><strong>基本使用举例</strong></blockquote>
<p>Frozensets在我们想要使用集合的情况下很有用,但需要一个不可变对象。<br>例如,如果没有Frozen sets我们不能定义其元素也是集合的集合(nested),因为集合元素必须是不可变的,会报错:</p>
<pre><code>>>> x1 = set(['foo'])
>>> x2 = set(['bar'])
>>> x3 = set(['baz'])
>>> x = {x1, x2, x3}
Traceback (most recent call last):
File "<pyshell#38>", line 1, in <module>
x = {x1, x2, x3}
TypeError: unhashable type: 'set'</code></pre>
<p>现在有了 Frozen sets,我们有了解决方案:</p>
<pre><code>>>> x1 = frozenset(['foo'])
>>> x2 = frozenset(['bar'])
>>> x3 = frozenset(['baz'])
>>> x = {x1, x2, x3}
>>> x
{frozenset({'bar'}), frozenset({'baz'}), frozenset({'foo'})}</code></pre>
<h2>总结</h2>
<p>这一期为大家讲了太多东西,一口老血吐在键盘上,总结不动了<br>只希望这期Set详解介绍可以帮助到大家,如果帮到了你,就点个赞吧~~<br>最后再次祝大家猪年大吉!!</p>
<hr>
<ol><li id="fn-1"> x3 ... <a href="#fnref-1" class="footnote-backref">↩</a>
</li></ol>
Python 进阶之路 (三) Tuple元组使用指南
https://segmentfault.com/a/1190000018107287
2019-02-08T08:00:00+08:00
2019-02-08T08:00:00+08:00
alpha94511
https://segmentfault.com/u/alpha94511
6
<h2>比List更安全的数据类型</h2>
<p>大家好,今天为大家介绍一种更为安全的Python内置数据类型:tuple(元组),以及它的基础用法</p>
<h2>元组是什么</h2>
<p>元组(tuple)是另一种有序的数据类型,与list比较类似。主要不同的一点是tuple被创建后就不能对其进行修改。所以,tuple与list不同,没有append(),pop(),insert()这些方法可以使用。获取元素的方法和list是一样的,可以通过索引来访问(也是从0开始的),只不过不能赋值成为其他的元素。</p>
<p><strong>因为 tuple不可变,所以代码更安全。如果可以的话,我们尽量使用tuple代替list。</strong></p>
<blockquote>创建元组</blockquote>
<pre><code># 定义一个空的tuple
t = ()
print(t)
Out:()</code></pre>
<p>只有1个元素的元组在进行定义的时候,需要加一个逗号 , 来消除歧义,否则定义的就不是一个元组而是元素本身</p>
<pre><code>t1 = (5)
t2 = (5, )
print(t1)
print(t2)
Out: 5
(5,)</code></pre>
<pre><code>tup4 = (1, 2, 3, 4, 5 ); # 创建时直接赋值
tup5 = "a", "b", "c", "d"; # 创建时直接赋值
print(tup4)
print(tup5)
Out:(1, 2, 3, 4, 5)
('a', 'b', 'c', 'd')</code></pre>
<p>一旦创建完,比如tup4 和 tup5 这两个tuple不能变了,它也没有append(),insert()这样的方法。其他获取元素的方法和List是一样的,我们可以正常地使用tup4[0],tup5[-1],但不能赋值成另外的元素</p>
<blockquote>访问元组</blockquote>
<p>访问元组和List基本一样,我们可以用切片很容易的查看元组中的元素,这里不多说,看个小栗子:</p>
<pre><code>tup4 = (1, 2, 3, 4, 5 );
tup5 = "a", "b", "c", "d";
print(tup4[0])
print(tup5[1:3])
print(tup5[::-1])
print(sorted(tup5,reverse=True)) # 使用sorted结果变成了List
Out: 1
('b', 'c')
('d', 'c', 'b', 'a')
['d', 'c', 'b', 'a']
</code></pre>
<blockquote>理解元组的不可变</blockquote>
<p>上面已经说过了,元组是不可变的,让我们来看下面这个小栗子:</p>
<pre><code> test=('a','b',[1,2,3])
print(test)
test[2][0]=100
print(test)
Out: ('a', 'b', [1, 2, 3])
('a', 'b', [100, 2, 3])
</code></pre>
<p>不知道有没有朋友会有疑问? 你不是说元组不能变嘛,这里怎么回事,test元组的第三个元素是List,这里我们修改了List的值结果不是变了吗?</p>
<p>这里要给大家说明一下,tuple不可变指的是指向不变,也就是说test[2]永远指向List[1,2,3],这里是因为List可变,所以我们才能修改为[100,2,3],但是改变前后test[2]的指向没有发生任何变化,如果我们想要直接改变test[2]的值,就会发现如下错误</p>
<pre><code>test[2]=[100,2,3]
Out: TypeError: 'tuple' object does not support item assignment
</code></pre>
<p>理解了“指向不变”后,如果我们需要创建一个内容也不变的tuple怎么做?那就必须保证tuple的每一个元素本身也不能变。</p>
<blockquote>元组的连接</blockquote>
<p>如前面所说,元组是不可改的,但是可以连接,我们可以使用 + 对元组进行连接:</p>
<pre><code>t1 = (2,3,5)
t2 = ('ricequant','python')
t3 = t1 + t2
print(t3)
Out:(2, 3, 5, 'ricequant', 'python')
</code></pre>
<blockquote>元组的删除</blockquote>
<p>元组中的元素不能被删除,但是我们可以使用 <strong>del</strong> 删除整个元组,删除后可以重新定义,非常简单,不多说啦</p>
<pre><code>person = ('xiaobai',18,'paris')
print(person)
del person
print(person)
Out: ('xiaobai', 18, 'paris')
NameError: name 'person' is not defined
</code></pre>
<blockquote>元组的解包<br>这里是比较有意思的地方,假设我们有一个元组t如下:</blockquote>
<pre><code>t = ('foo', 'bar', 'baz', 'qux')</code></pre>
<p>当我们创建 t 时,实际上就是一个打包,过程展示如下图:</p>
<p><img src="/img/bVbn8G7?w=1611&h=882" alt="clipboard.png" title="clipboard.png"></p>
<p>那如果是解包呢? 换过来就行了呀</p>
<pre><code>t = ('foo', 'bar', 'baz', 'qux')
(s1, s2, s3, s4) = t
print(s1,s2,s3,s4)
Out:foo bar baz qux</code></pre>
<p>当我们执行(s1, s2, s3, s4) = t的时候,实际发生的情况如下:</p>
<p><img src="/img/bVbn8G8?w=1023&h=894" alt="clipboard.png" title="clipboard.png"></p>
<p>这里注意一点,如果我们尝试解包一个元祖是传递的变量和元组实际元素数量不相符时会产生错误:</p>
<pre><code>(s1, s2, s3) = t
ValueError: too many values to unpack (expected 3)
(s1, s2, s3, s4, s5) = t
ValueError: not enough values to unpack (expected 5, got 4)
</code></pre>
<blockquote>元组的互换swap</blockquote>
<p>其实Python里面还有一种非常简单的创建元组的方法,那就是逗号,我们如果用逗号分隔一些元素,会自动生成一个元组:</p>
<pre><code>a = 'foo'
b = 'bar'
x= a, b
print(x)
Out:('foo', 'bar')
</code></pre>
<p>如果做一个简单的互换很容易,只要这样就可以了:</p>
<pre><code>x= b,a
print(x)
Out:('bar', 'foo')</code></pre>
<blockquote>元组的常用方法汇总</blockquote>
<ul>
<li>tup.index(x, [start, [stop]])) 返回元组中start到stop索引中第一个值为 x的元素在整个列表中的索引。如果没有匹配的元素就会返回一个错误。</li>
<li>tup.count(x) 返回 x 在元组中出现的次数。</li>
<li>cmp(tuple1, tuple2) 比较元组中两个元素。</li>
<li>len(tuple) 计算元组元素个数。</li>
<li>max(tuple) 返回元组中元素最大值。</li>
<li>min(tuple) 返回元组中元素最小值。</li>
<li>tuple(seq) 将列表转换为元组。</li>
<li>元组不提供字符串、列表和字典中的方法。如果相对元组排序,通常先得将它转换为列表并使其成为一个可变对象,才能获得使用排序方法,或使用sorted内置方法。</li>
</ul>
<h2>总结</h2>
<p>今天为大家讲解了我知道的有关tuple的一切,也为大家展示了一些常规操作,希望能够帮助到大家,马上就要到初五了,迎财神,吃饺子!!! 希望大家在2019大吉大利,大发横财!!</p>
Python 进阶之路 (二) Dict 进阶宝典
https://segmentfault.com/a/1190000018102693
2019-02-06T06:31:19+08:00
2019-02-06T06:31:19+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<h2>新年快乐</h2>
<p>大家好,今天是大年初二,身在国外没有过年的氛围,只能踏实写写文章,对社区做点贡献,在此祝大家新年快乐!上一期为大家梳理了一些List的进阶用法,今天我们来看字典Dict的相关技巧,我个人在编程中对字典的使用非常频繁,其实对于不是非常大的数据存储需求,字典是一个不错的选择,比List要快的多,我在基础篇里面讲过了一些关于dict的基础方法,如果没有看过的朋友们可以点击链接<a href="https://segmentfault.com/a/1190000018050011">Python 基础起步 (八) 字典实用技巧大全</a> ,好啦,闲话少说,现在让我们一起来看看今天的进阶技巧吧~</p>
<h2>字典进阶方法汇总</h2>
<blockquote>创建字典</blockquote>
<p>这里介绍最常见的几种方式,直接上例子:</p>
<pre><code>first = {} # 创建空字典
second = dict() # 创建空字典
keys = ['Name','Age','Job','Salary']
values = ['White',50,'cook',10000]
third=dict(zip(keys,values)) # Zip创建
fouth = dict(Name='White',Age=50,Job='cook',Salary=10000) # 等号创建
fifth = {1: {'name': 'John', 'age': '27', 'sex': 'Male'},
2: {'name': 'Marie', 'age': '22', 'sex': 'Female'}} # 创建一个嵌套字典
print(first)
print(second)
print(third)
print(fouth)
print(fifth[1])
Out: {}
{}
{'Name': 'White', 'Age': 50, 'Job': 'cook', 'Salary': 10000}
{'Name': 'White', 'Age': 50, 'Job': 'cook', 'Salary': 10000}
{'name': 'John', 'age': '27', 'sex': 'Male'}
</code></pre>
<p>这里我们可以直接用{}或者dict()创建空的字典,或者直接为字典以key:value的形式赋值,Zip和等号直接赋值也很方便,如果需要多层nested也可以很简单的实现,有关创建就说这么多啦</p>
<blockquote>字典排序</blockquote>
<p>有关字典排序,我们有两种选择,第一是根据字典的key值排序,第二是根据Value值排序,让我们一个个来看,首先让我们新建一个字典用于测试:</p>
<pre><code>final_result= dict(Math=80,Chinese=78,English=96,Science=60,Art=75)
print(final_result.items())
Out: dict_items([('Math', 80), ('Chinese', 78), ('English', 96), ('Science', 60), ('Art', 75)])
</code></pre>
<h5><strong>根据Key值排序</strong></h5>
<p>这里我们创建一个字典final_result,key值是科目的名字,value值是分数,首先根据Key值进行排序,首先让我们根据Key值升序,可选的方法很多,比如sorted, operator, lamba :</p>
<pre><code>print(sorted(final_result.items())) # 根据key的值升序
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]
</code></pre>
<pre><code>import operator
print(sorted(final_result.items(),key=operator.itemgetter(0)))
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]
</code></pre>
<pre><code>print(sorted(final_result.items(),key=lambda x:x[0]))
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]</code></pre>
<p>根据key值降序只要加个reverse=True就好了,因为sorted函数默认reverse=False,看下结果:</p>
<pre><code>print(sorted(final_result.items(),reverse=True)) # 根据key的值降序
Out:[('Science', 60), ('Math', 80), ('English', 96), ('Chinese', 78), ('Art', 75)]
</code></pre>
<pre><code>import operator
print(sorted(final_result.items(),key=operator.itemgetter(0),reverse=True))
Out:[('Science', 60), ('Math', 80), ('English', 96), ('Chinese', 78), ('Art', 75)]
</code></pre>
<pre><code>print(sorted(final_result.items(),key=lambda x:x[0],reverse=True))
Out:[('Science', 60), ('Math', 80), ('English', 96), ('Chinese', 78), ('Art', 75)]
</code></pre>
<p>有关lamba函数实在有太多可以总结的,我会在之后专门拿一期来讲,和filter reduce简直是神器,当我逐渐使用的多了后终于感受到了一点点pythonic的感觉,哈哈</p>
<h5><strong>根据Value值排序</strong></h5>
<p>其实大家看到了根据key的排序,也猜到了如何根据value 排序,让我们先看升序:</p>
<pre><code>print(sorted(final_result.items(),key=lambda x:x[1])) #根据Value升序
Out:[('Science', 60), ('Art', 75), ('Chinese', 78), ('Math', 80), ('English', 96)]
</code></pre>
<pre><code>import operator
print(sorted(final_result.items(),key=operator.itemgetter(1)))
Out:[('Science', 60), ('Art', 75), ('Chinese', 78), ('Math', 80), ('English', 96)]
</code></pre>
<p>降序也一样,无非就是加上reverse=True,这里不一一举例了:</p>
<pre><code>print(sorted(final_result.items(),key=lambda v:v[1],reverse=True))
Out:[('English', 96), ('Math', 80), ('Chinese', 78), ('Art', 75), ('Science', 60)]</code></pre>
<blockquote>字典合并(Merge)</blockquote>
<p>在Python 3.5以上可以直接用**,是一个常用的小技巧,在此对于2.7的用户说一声对不起,技术一直说是喜新厌旧呀,让我们看一个小栗子:</p>
<pre><code>def Merge(dict1, dict2):
res = {**dict1, **dict2}
return res
dict1 = {'a': 10, 'b': 8,'c':2}
dict2 = {'d': 6, 'c': 4}
dict3 = Merge(dict1, dict2)
print(dict3)
Out:{'a': 10, 'b': 8, 'c': 4, 'd': 6}
</code></pre>
<p>这里顺序很重要,大家一定要看好是谁覆盖了谁,如果我们交换一下顺序就会变成这样:</p>
<pre><code>def Merge(dict1, dict2):
res = {**dict2, **dict1} # 交换了顺序
return res
dict1 = {'a': 10, 'b': 8,'c':2}
dict2 = {'d': 6, 'c': 4}
dict3 = Merge(dict1, dict2)
print(dict3)
Out:{'d': 6, 'c': 2, 'a': 10, 'b': 8}
</code></pre>
<p>对于Python2的朋友们不用担心,自然有解决方案,那就是用update函数,也很方便,上代码:</p>
<pre><code>dict1 = {'a': 10, 'b': 8,'c':2}
dict2 = {'d': 6, 'c': 4}
dict2.update(dict1)
print(dict2)
Out:{'d': 6, 'c': 2, 'a': 10, 'b': 8}
</code></pre>
<blockquote>利用Json.dumps()美化输出dict</blockquote>
<p>我们如果碰到以下这种情况的dict,如果按照常规print输出会这样:</p>
<pre><code>my_mapping = {'a': 23, 'b': 42, 'c': 0xc0ffee}
print(my_mapping)
Out:{'a': 23, 'b': 42, 'c': 12648430}
</code></pre>
<p>但是如果我们能引用json库里的dumps方法会得到好的多的效果:</p>
<pre><code>import json
print(json.dumps(my_mapping, indent=4, sort_keys=True))
Out:{
"a": 23,
"b": 42,
"c": 12648430
}
</code></pre>
<blockquote>字典参数解包</blockquote>
<p>Python里面方便神奇的方法很多,比如下面这个,可以实现解包字典:</p>
<pre><code>def unpack(k1,k2,k3):
print(k1,k2,k3)
my_dict = {'k1':'value1','k2':'value2','k3':'value3'}
unpack(**my_dict)
Out: value1 value2 value3
</code></pre>
<p>顺便提一下哈,有关 args和kwargs的方法我会专门在后面的一期讲,敬请期待!</p>
<blockquote>字典推导式</blockquote>
<p>这个我写的比较纠结,因为咨询了我的主管,他推荐我尽量不要用,我也不太懂其中原因,不知道有没有大神可以出来解答一下哈,具体用法和List的推导式一样,上代码:</p>
<pre><code>import json
first = {x:'A'+str(x) for x in range(8)}
print(json.dumps(first,indent=4, sort_keys=True)) # 这种情况用json输出好看些
Out:{
"0": "A0",
"1": "A1",
"2": "A2",
"3": "A3",
"4": "A4",
"5": "A5",
"6": "A6",
"7": "A7"
}</code></pre>
<p>或者可以这么用:</p>
<pre><code>second={v:k for k,v in first.items()}
print(json.dumps(second,indent=4))
Out:{
"A0": 0,
"A1": 1,
"A2": 2,
"A3": 3,
"A4": 4,
"A5": 5,
"A6": 6,
"A7": 7
}
</code></pre>
<p>至于其他乱七八糟的用法大家可以自己去想哈哈</p>
<h2>总结</h2>
<p>今天系统地为大家梳理了几点:</p>
<ul>
<li>创建字典不同方法</li>
<li>字典排序</li>
<li>字典合并</li>
<li>字典解包</li>
<li>json优化输出</li>
<li>字典推导式</li>
</ul>
<p>希望可以帮到大家,后续如果我发想有什么有意思的方法和技巧我会加上,如果大家对Dict<br>的其他使用技巧感兴趣,可以关注我的微信公众号Python极简教程,我会把最高效,简洁的小技巧一一记录下来,分享给大家:</p>
<p><img src="/img/bVbwxMn?w=258&h=258" alt="图片描述" title="图片描述"></p>
Python 进阶之路 (一) List 进阶方法汇总,新年快乐!
https://segmentfault.com/a/1190000018098698
2019-02-04T00:22:00+08:00
2019-02-04T00:22:00+08:00
alpha94511
https://segmentfault.com/u/alpha94511
11
<h2>开启变身模式</h2>
<p>大家好, 从这一期开始,我们会从小白变身为中等小白,在基础起步阶段有太多的东西我没有讲到,但是俗话说的好,无他,但手熟尔,只要多多练习,时间会是最好的证明,相信我们终有一天会成为高手,因此从这一系列开始,让我们一起更上一层楼,还是和往常一样,我也是水平不高,如果有大神发现文章中的错误一定要指出哈~</p>
<p>先皮一下,看个欧阳修的<<卖油翁>>:</p>
<blockquote>陈康肃公尧咨善射,当世无双,公亦以此自矜。尝射于家圃,有卖油翁释担而立,睨之,久而不去。见其发矢十中八九,但微颔之。<p>康肃问曰:“汝亦知射乎?吾射不亦精乎?”翁曰:“无他,但手熟尔。”康肃忿然曰:“尔安敢轻吾射!”翁曰:“以我酌油知之。”乃取一葫芦置于地,以钱覆其口,徐以杓酌油沥之,自钱孔入,而钱不湿。因曰:<strong>“我亦无他,惟手熟尔。</strong>”康肃笑而遣之。</p>
</blockquote>
<h2>List的进阶用法</h2>
<p>这里我将会详细介绍一些我认为非常不错的List的使用方法,至于list 自带的一些基础用法,这里不再说明,感兴趣的朋友们可以看看我的基础教程: <a href="https://segmentfault.com/a/1190000018002749">Python 基础起步 (五) 一定要知道的数据类型:初识List</a> 和 <a href="https://segmentfault.com/a/1190000018016421">Python 基础起步 (六) List的实用技巧大全</a>, 好啦,闲话少说,让我们开始吧</p>
<blockquote><strong>把其他类型数据结构转化为List类型</strong></blockquote>
<p>利用<strong>list(target)</strong>即可实现把其他类型的数据结构转化为List类型,十分实用,我们可以把字符串,元组等数据结构转化为List,也可以直接创建,像下图一样:</p>
<pre><code>
print(list()) # 创建空List
vowelString = 'aeiou' # 把字符串转化为List
print(list(vowelString))
vowelTuple = ('a', 'e', 'i', 'o', 'u') # 从元组tuple转化为List
print(list(vowelTuple))
vowelList = ['a', 'e', 'i', 'o', 'u'] # 从List到List
print(list(vowelList))
vowelSet = {'a', 'e', 'i', 'o', 'u'} # 从Set到List
print(list(vowelSet))
Out: []
['a', 'e', 'i', 'o', 'u']
['a', 'e', 'i', 'o', 'u']
['a', 'e', 'i', 'o', 'u']
['a', 'e', 'i', 'o', 'u']
</code></pre>
<p>可能平时用的比较多的一般是从一个dict中提取 keys 和 values :</p>
<pre><code>person = {
'name':'Peppa Pig',
'age':15,
'country':'bratian'}
person_keys = list(person.keys())
person_values = list(person.values())
print(list(person)) # 如果直接转化一个字典只会把keys提取出来
print(list(person.keys()))
print(list(person.values()))
Out:['name', 'age', 'country']
['name', 'age', 'country'] # 把字典中的Keys提出
['Peppa Pig', 15, 'bratian'] # 把字典中的Values提出
</code></pre>
<p>这里大家稍微注意下,如果直接用list(person)会默认等同于person.keys()</p>
<blockquote><strong>List排序方法汇总</strong></blockquote>
<p>总体来说,有两种方法最为便捷,<strong>List.sort </strong>或者 <strong>sorted(List)</strong>,具体来看如何实现,先看升序:</p>
<pre><code>vowels = ['e', 'a', 'u', 'o', 'i']
vowels.sort()
print('Sorted list Acending:', vowels) # 使用sort
Out: Sorted list Acending: ['a', 'e', 'i', 'o', 'u']</code></pre>
<pre><code>vowels = ['e', 'a', 'u', 'o', 'i']
print('Sorted list Acending:', sorted(vowels)) # 使用sorted
Out:Sorted list Acending: ['a', 'e', 'i', 'o', 'u']
</code></pre>
<p>再来看降序:</p>
<pre><code>vowels = ['e', 'a', 'u', 'o', 'i']
vowels.sort(reverse=True) # 使用sort
print('Sorted list (in Descending):', vowels)
Out: Sorted list (in Descending): ['u', 'o', 'i', 'e', 'a']
</code></pre>
<pre><code>vowels = ['e', 'a', 'u', 'o', 'i']
print('Sorted list (in Descending):', sorted(vowels,reverse=True)) # 使用sorted方法
Out:Sorted list (in Descending): ['u', 'o', 'i', 'e', 'a']</code></pre>
<p>其实这里里面还有一个关键的参数是key,我们可以自定义一些规则排序,下面看个例子,用的是默认的len函数<br>,让我们根据List中每个元素的长度来排序,首先是升序:</p>
<pre><code>vowels = ['I', 'live', 'at', 'Paris', 'I','love','Python']
# 使用sort
vowels.sort(key=len)
print('Sorted by lenth Ascending:', vowels)
Out:Sorted by lenth: ['I', 'I', 'at', 'live', 'love', 'Paris', 'Python']
</code></pre>
<pre><code>vowels = ['I', 'live', 'at', 'Paris', 'I','love','Python']
# 使用 sorted
print('Sorted by lenth Ascending:', sorted(vowels,key=len))
Out:Sorted by lenth Ascending: ['I', 'I', 'at', 'live', 'love', 'Paris', 'Python']
</code></pre>
<p>降序也差不多啦:</p>
<pre><code>vowels = ['I', 'live', 'at', 'Paris', 'I','love','Python']
vowels.sort(key=len,reverse=True)
print('Sorted by lenth Descending:', vowels)
Out:Sorted by lenth Descending: ['Python', 'Paris', 'live', 'love', 'at', 'I', 'I']
</code></pre>
<pre><code>vowels = ['I', 'live', 'at', 'Paris', 'I','love','Python']
print('Sorted by lenth Descending:', sorted(vowels,reverse=True))
Out:Sorted by lenth Descending: ['Python', 'Paris', 'live', 'love', 'at', 'I', 'I']
</code></pre>
<p>有关这个key我们可以自己定义,请看下面的大栗子:</p>
<pre><code>def takeSecond(elem):
return elem[1]
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
random.sort(key=takeSecond) # sort list with key
print('Sorted list:', random)
Out: [(4, 1), (2, 2), (1, 3), (3, 4)]</code></pre>
<p>这里因为列表中的元素都是元组,所以我们规定按照元组的第二个数排序,所以结果如上,默认依然是reverse=False ,也就是升序,至于其他有意思的方法大家可以自己开发</p>
<blockquote><strong>逆序输出List</strong></blockquote>
<p>如果我们想要逆序输出一个List,简单来说有两种方法比较好用,切片和reverse,来看例子:</p>
<pre><code> vowels = ['I', 'live', 'at', 'Paris', 'I','love','Python']
print(list(reversed(vowels)))
print(list[::-1])
Out:['Python', 'love', 'I', 'Paris', 'at', 'live', 'I']
['Python', 'love', 'I', 'Paris', 'at', 'live', 'I'] </code></pre>
<blockquote><strong>利用Range生成List</strong></blockquote>
<p>Python里面为我们提供了一个超好用的方法range(),具体的用法很多,不在这里一一讲解啦,但是大家可以看一下它如何和List结合在一起的:</p>
<pre><code>five_numbers=list(range(5)) #创建0~4的List
print(five_numbers)
odd_numbers=list(range(1,50,2)) #创建0~50所有的奇数的List
print(odd_numbers)
even_numbers=list(range(0,50,2)) #创建0~50所有的偶数的List
print(even_numbers)</code></pre>
<p>其实很简单,用的最多的就是range(start, stop, hop)这个结构,其他的大家可以自行查询哈</p>
<blockquote><strong>List列表推导式</strong></blockquote>
<p>其实这个东西无非就是为了减少代码书写量,比较Pythonic的东西,基础模板如下:</p>
<pre><code>variable = [out_exp for out_exp in input_list if out_exp == 2]</code></pre>
<p>大家直接看比较麻烦,还是直接上代码吧:</p>
<pre><code>S = [x**2 for x in range(8)] # 0~7每个数的平方存为List
V = [2**i for i in range(8)] # 2的 0~7次方
M = [x for x in S if x % 2 == 0] #找出在S里面的偶数
print(S)
print(V)
print(M)
Out: Square of 0 ~7: [0, 1, 4, 9, 16, 25, 36, 49]
The x power of 2,(0<x<7) : [1, 2, 4, 8, 16, 32, 64, 128]
The even numbers in S: [0, 4, 16, 36]
</code></pre>
<p>通过这个小栗子大家估计已经明白用法啦,推导式还可以这么用:</p>
<pre><code>verbs=['work','eat','sleep','sit']
verbs_with_ing=[v+'ing' for v in verbs] # 使一组动词成为现在分词
print(verbs_with_ing)
Out:['working', 'eating', 'sleeping', 'siting']
</code></pre>
<p>或者加上查询条件if :</p>
<pre><code>all_numbers=list(range(-7,9))
numbers_positive = [n for n in all_numbers if n>0 ]
numbers_negative = [n for n in all_numbers if n<0]
devided_by_two = [n for n in all_numbers if n%2==0]
print("The positive numbers between -7 to 9 are :",numbers_positive)
print("The negative numbers between -7 to 9 are :",numbers_negative )
print("The numbers deveided by 2 without reminder are :",devided_by_two)```
Out:The positive numbers between -7 to 9 are : [1, 2, 3, 4, 5, 6, 7, 8]
The negative numbers between -7 to 9 are : [-7, -6, -5, -4, -3, -2, -1]
The numbers deveided by 2 without reminder are : [-6, -4, -2, 0, 2, 4, 6, 8]
</code></pre>
<h2>总结</h2>
<p>其实客观来讲,list在实际项目中所用的地方有限,特别是处理数据量较大的情况下,因为速度极慢,但是在一些边边角角的地方我个人用的还挺多,尤其是不求速度的地方,总的来讲List是我个人比较喜欢的Python<br>数据结构,简单好用!!!!!</p>
<p>如果大家对List的其他使用技巧感兴趣,可以关注我的微信公众号Python极简教程,我会把最高效,简洁的小技巧一一记录下来,分享给大家:</p>
<p><img src="/img/bVbwxMn?w=258&h=258" alt="图片描述" title="图片描述"></p>
Python 基础起步 (十一) 写在最后的话(附优秀资源汇总)
https://segmentfault.com/a/1190000018083172
2019-02-01T06:18:58+08:00
2019-02-01T06:18:58+08:00
alpha94511
https://segmentfault.com/u/alpha94511
4
<h2>总结</h2>
<p>不知不觉作为一个小白已经写完了最最基础的Python简单教程,如果你也和我一样从事金融行业,或者毫无编程基础的小白,希望我的教程可以帮到你,这是Python基础起步的最终篇,我希望能和大家简单介绍一下后续介绍以及很多帮助到我的优秀的平台。</p>
<h2>后续计划</h2>
<p>在大家已经了解到最最基础的Python知识后,剩下的就是多多练习了,我打算在接下来会先开通两个专栏,一个是Python进阶系列,还是一个就是关于我每天都在用的Pandas库的技巧分享,在接下来的Python进阶系列,我打算为大家梳理汇总以下几个方面的知识:</p>
<blockquote>Python进阶系列涉及知识点</blockquote>
<ul>
<li>List 实用技巧汇总</li>
<li>Dict 实用技巧汇总</li>
<li>Tuple & Set 使用指南</li>
<li>Map,Filetr,Reduce详解</li>
<li>Lamba 详解</li>
<li>Decorator 实用指南</li>
<li>Collection模块的进阶使用:defaultDict,OrderedDict, namedtuple...</li>
<li>OOP 编程介绍,class基础</li>
<li>
<em>args and </em>*kwargs</li>
</ul>
<p>在这个系列我依然会全力以赴,我会尽我所能把自己学习到的,搜集到的融合在一起,希望大家喜欢。</p>
<blockquote>优秀资源分享</blockquote>
<p>在我的学习过程中,大量参考了以下优秀网站和资源,在此分享给大家:</p>
<ul>
<li>
<a href="https://link.segmentfault.com/?enc=QHCOhd9bavIx95CUrQXN6Q%3D%3D.t1xdw7szPTb5bsXhbznsoIsPtugURVH4V7zXPgY%2BOlg%3D" rel="nofollow">Corey Schafer的youtube视频教程</a>,个人认为是最好的视频教程,不接受反驳</li>
<li>
<a href="https://link.segmentfault.com/?enc=QvjqlXcgtbRZS3XukpllzQ%3D%3D.339sFyPW8sa5w%2Fo751J3p2hbYS7Ji7K4ak6zDJszbTk%3D" rel="nofollow">Py4e</a>,密歇根大学的教程,有详细PPT 可以下载</li>
<li>
<a href="https://link.segmentfault.com/?enc=36tvQXNA%2Fs%2BeETvG8PMikg%3D%3D.6wERi1aAnahNwm%2FhCAo8TOIADgapmYJjsNCpqpPqdbw%3D" rel="nofollow">RealPython</a> 由浅入深来学习,最爱的教学网站</li>
<li>
<a href="https://link.segmentfault.com/?enc=L7bVILnF8zS%2BlwyJAcBAww%3D%3D.ypj%2BEsfUxWjmS0LXlhck0SHaq5kJX0OhW7Tr4p8VBvY%3D" rel="nofollow">DataCamp</a> 不错的免费教程</li>
<li>
<a href="https://link.segmentfault.com/?enc=CLxz35kNBR8hyGno0KTE8g%3D%3D.NQvDBHt%2BNMw3g09d4Ulzc5IcLeo%2FljgRQP112nU69klgEdjwLkJoeVMCk99sVug7" rel="nofollow">Tutorial Point</a>不错的免费教程</li>
</ul>
<p>完结,撒花~</p>
Python 基础起步 (十) 什么叫函数?
https://segmentfault.com/a/1190000018083128
2019-02-01T05:13:24+08:00
2019-02-01T05:13:24+08:00
alpha94511
https://segmentfault.com/u/alpha94511
4
<h2>Python 函数</h2>
<p>大家好,这一期让我们来看看Python中比较核心的知识点:函数(也叫方法),那么到底什么是一个函数呢?先让我引入一个官方一点的定义:</p>
<blockquote>函数是对程序逻辑进行结构化或者过程化的一种编程方法。是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。</blockquote>
<p>好啦,我知道这说的不是人话,其实简单理解就是: 如果我们有一些代码可以执行一些功能,比如有100行,我们可以把这些代码打包叫做A,那么A就是一个函数,下次我们想要执行一样的功能,不用重新写100行,直接调用A就可以了。在接下来的介绍中让我们看看实际例子</p>
<h3>内置函数,调用函数</h3>
<p>首先,Python内置了很多有用的函数,我们可以直接调用。不需要创建。而要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs</p>
<pre><code>abs(-500)
Out:500</code></pre>
<p>很简单吧,只要直接调用函数名和函数需要的参数即可,再看一些其他的内置函数的例子:</p>
<pre><code>type('Hello') # <class 'str'>
max(10,20,30,45) # 45
len('Hello world') # 11
</code></pre>
<p>大家看到常用的type函数了吧~,还有很多都是非常好用的内置函数,大家可以自己谷歌百度一下,在实际编程中非常有效,不过要注意一点,如果参数传递错误,那就会出错:</p>
<pre><code>abs(1, 5)
TypeError: abs() takes exactly one argument (2 given)
</code></pre>
<p>这里很好理解,abs这个内置方法只接受1个参数,这里传递了两个所以就报错了。</p>
<h3>如何定义一个函数</h3>
<p>我们可以自己创建函数,创建一个函数很简单,利用关键字def就可以, 之后依次写出函数名、括号、括号中的参数和冒号,最后在缩进块中编写函数体,函数的返回值用<code>return</code>语句返回。当然,如果不需要返回结果值,也可以不加<code>return</code>。我们以自定义一个求绝对值的<code>my_abs</code>函数为例:</p>
<pre><code>def my_abs(x):
if x >= 0:
return x
else:
return -x
</code></pre>
<p>这里大家就可以发现,我们之前所学到的逻辑条件排上了用场,这里我们根据绝对值的求法写出了自己的函数,如果是正数返回函数本身,如果是负数,返回它的相反数。让我们测试一下</p>
<pre><code>my_abs(-200)
Out:200</code></pre>
<p>这里需要注意几点:函数体内部的语句在执行时,一旦执行到<code>return</code>时,函数就执行完毕,并将结果返回。因此,函数内部通过条件判断和循环可以实现非常复杂的逻辑。</p>
<ul>
<li>如果没有<code>return</code>语句,函数执行完毕后也会返回结果,只是结果为<code>None</code>。</li>
<li>return None<code>可以简写为</code>return`。</li>
</ul>
<h3>函数的参数</h3>
<p>定义函数的时候,我们把参数的名字和位置确定下来,函数的接口定义就完成了。对于函数的调用者来说,只需要知道如何传递正确的参数,以及函数将返回什么样的值就够了,函数内部的复杂逻辑被封装起来,调用者无需了解。</p>
<p>Python的函数定义非常简单,但灵活度却非常大。除了正常定义的必选参数外,还可以使用默认参数、可变参数和关键字参数,使得函数定义出来的接口,不但能处理复杂的参数,还可以简化调用者的代码。</p>
<blockquote>默认参数</blockquote>
<p>我们仍以具体的例子来说明如何定义函数的默认参数。我们现在想写一个求平方的函数:</p>
<pre><code>def power(x):
return x * x</code></pre>
<p>测试一下是否正确</p>
<pre><code>power(5)
Out:25</code></pre>
<p>现在,如果我们要计算 x3x3 怎么办?可以再定义一个power3函数,但是如果要计算 x4x4 、 x5x5 ……怎么办?我们不可能定义无限多个函数。你也许想到了,可以把 power(x) 修改为 power(x, n),用来计算 xn:</p>
<pre><code>def power(x, n):
s = 1
while n > 0:
n = n - 1
s = s * x
return s
</code></pre>
<p>对于这个修改后的 power 函数,可以计算任意 n 次方:</p>
<pre><code>power(2, 3)
Out:8</code></pre>
<p>但是,旧的调用代码失败了,原因是我们增加了一个参数,导致旧的代码无法正常调用:</p>
<pre><code>power(9)
TypeError: power() missing 1 required positional argument: 'n'
</code></pre>
<p>这个时候,默认参数就排上用场了。由于我们经常计算 x2x2 ,所以,完全可以把第二个参数 n 的默认值设定为 2:</p>
<pre><code>def power(x, n=2):
s = 1
while n > 0:
n = n - 1
s = s * x
return s</code></pre>
<p>这样,当我们调用 power(5) 时,相当于调用 power(5, 2):</p>
<pre><code>power(5)
Out:25
</code></pre>
<p>而对于 n > 2 的其他情况,就必须明确地传入 n,比如 power(5, 3)。从上面的例子可以看出,默认参数可以简化函数的调用。设置默认参数时,有几点要注意:</p>
<ul>
<li>必选参数在前,默认参数在后,否则Python的解释器会报错(思考一下为什么默认参数不能放在必选参数前面)</li>
<li>如何设置默认参数</li>
</ul>
<p>好啦,今天先为大家讲到这里,有很多复杂的方法还要大家自己去探索,</p>
Python 基础起步 (九) 条件语句 if elif else 其实很简单
https://segmentfault.com/a/1190000018062341
2019-01-30T05:43:10+08:00
2019-01-30T05:43:10+08:00
alpha94511
https://segmentfault.com/u/alpha94511
5
<p>大家好,在我们上一篇复习了dict的基础和实用技巧后,今天我们来看一下Python里的逻辑关系,所谓逻辑关系无非就是如果...就...否则...之类的,不是非常复杂,我的意思是复杂的地方不用目前了解,因为已经超出了小白的水准,哈哈,闲话少说,让我们一起来看看吧~</p>
<h2>什么是if</h2>
<blockquote>Python里面用if-else的形式</blockquote>
<pre><code>if <expr>: #如果满足什么条件
<statement> #就执行什么步骤</code></pre>
<p>注意有两点:</p>
<ul>
<li>if 后面的判断结果就是True和False两种类型,如果True,将执行statement的步骤1</li>
<li>statement那里一定有缩进,写在if条件里面</li>
</ul>
<p>让我们拿实际的情况看看具体是什么意思:</p>
<pre><code>x = 0
y = 5
if x < y: # True
print('yes')
if y < x: # False
print('yes')
if x: # Falsy
print('yes')
if y: # Truthy
print('yes')
if x or y: # Truthy
print('yes')
if x and y: # Falsy
print('yes')
if 'aul' in 'grault': # Truthy
print('yes')
if 'quux' in ['foo', 'bar', 'baz']: # Falsy
print('yes')</code></pre>
<p>我给大家解释一个可能产生的疑问,就是如下这个条件式:</p>
<pre><code>if x: # Falsy
print('yes')</code></pre>
<p>这里为什么是错误的呢,因为很简单,在Python里默认1就是True,0就是False,这里x的值正好是0,和ython默认的规定的一样,所以这个判断也是错的,大家明白了吧~</p>
<blockquote>满足if条件后有多个statements执行</blockquote>
<p>用大白话讲就是说如果满足了if的条件,那么你有很多事情想依次执行,而且在做完这些事情后完成了条件,开始下一项任务,那么这种情况下调用形式如下图:</p>
<pre><code>if <expr>:
<statement>
<statement>
...
<statement>
<following_statement></code></pre>
<p><img src="/img/bVbnWZX?w=1161&h=567" alt="clipboard.png" title="clipboard.png"></p>
<p>感觉还是图片比较容易理解哈哈~ 让我们来看一个真实的例子:</p>
<pre><code>if 'foo' in ['bar', 'baz', 'qux']:
print('Expression was true')
print('Executing statement in suite')
print('...')
print('Done.')
print('After conditional')</code></pre>
<p>这段代码完美的展示了图片中讲解的内容,大家可以猜猜结果,这段代码执行后会输出什么。<br>现在公布答案:</p>
<pre><code>After conditional</code></pre>
<p>简单说一下,很简单,我们想要看看'foo' 这个字符串是否在数组['bar', 'baz', 'qux']里面,如果在,一次执行底下的缩进的4个print方法,如果不在,跳出if,继续该干什么干什么,所以显然易见'foo'不在数组里,因此直接执行最后的print('After conditional')</p>
<blockquote>多个if在一起</blockquote>
<p>大家看一下下面的代码,如果你预想的输出结果和实际的一样,那恭喜你已经彻底明白if是什么东西了</p>
<pre><code># Does line execute? Yes No
# --- --
if 'foo' in ['foo', 'bar', 'baz']: # x
print('Outer condition is true') # x
if 10 > 20: # x
print('Inner condition 1') # x
print('Between inner conditions') # x
if 10 < 20: # x
print('Inner condition 2') # x
print('End of outer condition') # x
print('After outer condition') # x</code></pre>
<p>输出结果为:</p>
<pre><code>Outer condition is true
Between inner conditions
Inner condition 2
End of outer condition
After outer condition</code></pre>
<h2>if else</h2>
<p>在Python中使用if else 的表达模板如下:</p>
<pre><code>if <expr>:
<statement(s)>
else:
<statement(s)></code></pre>
<p>大家想必很容易看懂,现在举个栗子:</p>
<pre><code>x = 20
if x < 50:
print('(first suite)')
print('x is small')
else:
print('(second suite)')
print('x is large')</code></pre>
<p>输出结果为:</p>
<pre><code>(first suite)
x is small</code></pre>
<p>很容易理解了,如果满足if条件如何,else否则怎么怎么样</p>
<h2>if,elif ,else</h2>
<p>让我们来看最后一种情况,也很好弄懂,elif的意思就是else if 的缩写而已,表达模式如下:</p>
<pre><code>if <expr>:
<statement(s)>
elif <expr>:
<statement(s)>
elif <expr>:
<statement(s)>
...
else:
<statement(s)></code></pre>
<p>看一个具体实例:</p>
<pre><code>name = 'Joe'
if name == 'Fred':
print('Hello Fred')
elif name == 'Xander':
print('Hello Xander')
elif name == 'Joe':
print('Hello Joe')
elif name == 'Arnold':
print('Hello Arnold')
else:
print("I don't know who you are!")</code></pre>
<p>输出结果为:</p>
<pre><code>Hello Joe</code></pre>
<p>总而言之就是如果不满足第一个if条件,接着看是否满足第二个if条件,然后是第三个...</p>
<h2>if 写在一行里</h2>
<p>Python有一个好处就是它非常懒,很多东西它会自己想办法减少代码量,所以就出现了以下这种写法:</p>
<pre><code>if <expr>: <statement>
if <expr>: <statement_1>; <statement_2>; ...; <statement_n></code></pre>
<p>分别看两个例子,第一个简单一些的:</p>
<pre><code>if 'f' in 'foo': print('1'); print('2'); print('3')
Out:1
2
3</code></pre>
<p>稍微复杂一些的:</p>
<pre><code>x = 2
if x == 1: print('foo'); print('bar'); print('baz')
elif x == 2: print('qux'); print('quux')
else: print('corge'); print('grault')
Out: qux
quux</code></pre>
<h2>总结</h2>
<ul>
<li>基本的条件语句常见的就这几种</li>
<li>个人建议不要写一行,还是拆成多行写,这样思路清晰</li>
</ul>
Python 基础起步 (八) 字典实用技巧大全,申精干货,必看!
https://segmentfault.com/a/1190000018050011
2019-01-29T06:13:18+08:00
2019-01-29T06:13:18+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<p>Hello ,同为小白的朋友们大家好,这期主要为大家带来有关字典的一些实用技巧。上篇笔记通过感人肺腑的老白让大家认识到了字典这么有用的数据类型,这一期让我们看看如何能更好的操作字典。</p>
<h2>基础篇</h2>
<p>下面我给大家来一个简单的字典方法汇总,诚意满满,希望大家喜欢,如果有错误或者遗漏希望大神可以补上~</p>
<blockquote>创建字典,得到字典中的键值,新增,修改,删除,清空字典</blockquote>
<pre><code>stocks ={'IBM':146, # 创建字典
'MSFT':44,
'MAOTAI':1000}
stocks['IBM'] # 查询字典中的一个值 Out:146
stocks.get('MSFT') # 查询字典中的一个值 Out:44
stocks.get('a','Not found') # 查询字典中的一个值,如果没有,返回 Not found
stocks['a'] # 抛出错误,因为字典中没有这个值
stocks['IBM']=200 # 修改值
stocks['APPLE']=400 # 新增键和值
stocks.update({'APPLE':900,
'MAOTAI':0,
'MSFT':250}) # 一次修改多个值
del stocks['APPLE'] # 删除一个值
stocks.pop['MAOTAI'] # 删除并返回删除的值
stocks.keys() # 获得字典的所有键
stocks.values() # 获得字典的所有值
stocks.items() # 获得字典的所有键和值,形式为 (key,value)
for k,v in stocks.items(): # 遍历一个字典,输出它的键和值
print(k,v)
stocks.clear() #清空一个字典,让字典为空
del stocks #直接删除一个字典</code></pre>
<p>好啦,基础方法就这么多大概,这里创建了一个stocks字典用来表示股票名称和价钱,然后在基础上举例子的,有两点需要大家注意:</p>
<ul>
<li>一次更新多个值的时候用update()方法会很方便</li>
<li>如果利用dict[key]的方式获取一个字典中不存在的值会报错,正确做法是用get()函数,效果好很多</li>
</ul>
<h2>奇淫巧技篇</h2>
<blockquote>创建字典歪门邪道之一: list + zip</blockquote>
<p>除了常规的方式,还有很多其他方式可以帮助我们创建一个字典,比如我们这里想要创建一个字典来表示一个学生的各科成绩:</p>
<pre><code>subject=['Math','Chinese','English','Science','Art']
marks=[80,78,96,60,75]
final_result=dict(zip(subject,marks))
print(final_result.items())
Out: dict_items([('Math', 80), ('Chinese', 78), ('English', 96), ('Science', 60), ('Art', 75)])
</code></pre>
<p>这里其实很容易看懂,我们把subject的所有值作为<strong>key</strong>值,把marks所有值看成<strong>values</strong>的值,最后用Python很常见的zip就啪啪啪的将他们合并啦,形成了一个新的字典final_result</p>
<blockquote>创建字典歪门邪道之二: 利用等号</blockquote>
<p>上面的字典还可以通过这种形式来创建;</p>
<pre><code>final_result= dict(Math=80,Chinese=78,English=96,Science=60,Art=75)
print(final_result.items())
Out: dict_items([('Math', 80), ('Chinese', 78), ('English', 96), ('Science', 60), ('Art', 75)])
</code></pre>
<blockquote>字典排序: 根据键值Key排序</blockquote>
<p>还是使用上面已经建好的字典 final_result,我们先根据字典中的键值来排序:</p>
<pre><code>print(sorted(final_result.items())) # 自动根据键的值从小到大或者按照A-Z排序
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]
</code></pre>
<p>换一种方式:</p>
<pre><code>import operator
print(sorted(final_result.items(),key=operator.itemgetter(0)))
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]
</code></pre>
<p>或者用lamba函数秀一波:</p>
<pre><code>print(sorted(final_result.items(),key=lambda x:x[0]))
Out:[('Art', 75), ('Chinese', 78), ('English', 96), ('Math', 80), ('Science', 60)]</code></pre>
<p>不要问我为什么,我也在学,但是我深知在Python编程装逼界你不用几个lamba,reduce什么的都不好意思和人打招呼,哈哈</p>
<p>如果我们想要实现根据键值倒序也很简单:</p>
<pre><code>print(sorted(final_result.items(),reverse=True))
Out:[('Science', 60), ('Math', 80), ('English', 96), ('Chinese', 78), ('Art', 75)]
</code></pre>
<blockquote>字典排序: 根据Value值排序</blockquote>
<p>其实大家看到了根据key的排序,也猜到了如何根据value 排序:</p>
<pre><code>print(sorted(final_result.items(),key=lambda x:x[1])) #根据分数排序,从低到高
Out:[('Science', 60), ('Art', 75), ('Chinese', 78), ('Math', 80), ('English', 96)]
</code></pre>
<p>或者换一种方式:</p>
<pre><code>import operator
print(sorted(final_result.items(),key=operator.itemgetter(1)))
Out:[('Science', 60), ('Art', 75), ('Chinese', 78), ('Math', 80), ('English', 96)]
</code></pre>
<p>倒序也是类似:</p>
<pre><code>print(sorted(final_result.items(),key=lambda kv:kv[1],reverse=True))
Out:[('English', 96), ('Math', 80), ('Chinese', 78), ('Art', 75), ('Science', 60)]
</code></pre>
<p>这里我在lamba后面没有写x,写了一个kv,就是告诉大家这里是什么不太重要,先这么理解就行</p>
<blockquote>获取字典的子集</blockquote>
<p>我们将会一直使用上面的final_result字典来示范,这里如果我们想要得到这个学生成绩大于70分的字典子集应该怎么做呢,很简单:</p>
<pre><code>final_result= dict(Math=80,Chinese=78,English=96,Science=60,Art=75)
above_seventy ={subject:mark for subject,mark in final_result.items() if mark >70}
print(above_seventy.items())
Out:dict_items([('Math', 80), ('Chinese', 78), ('English', 96), ('Art', 75)])
</code></pre>
<blockquote>交换字典中的key和value的值</blockquote>
<p>这个小技巧可以这样实现:</p>
<pre><code>exchange_key_value= dict(zip(final_result.values(),final_result.keys()))
print(exchange_key_value.items())
Out: dict_items([(80, 'Math'), (78, 'Chinese'), (96, 'English'), (60, 'Science'), (75, 'Art')])
</code></pre>
<blockquote>获取字典最大值最小值</blockquote>
<p>简直不要太简单呀:</p>
<pre><code>print("The best mark is {}".format(max(final_result.values())))
print("The worst mark is {}".format(min(final_result.values())))
Out: The best mark is 96
The worst mark is 60</code></pre>
<h2>总结</h2>
<p>没啥可说的啦,写了这么多,求老铁们双击666鼓励一波!!!!<br>完结,撒花~</p>
Python 基础起步 (七) 初识字典 Dictionary(绝命毒师前传)
https://segmentfault.com/a/1190000018033955
2019-01-27T05:10:47+08:00
2019-01-27T05:10:47+08:00
alpha94511
https://segmentfault.com/u/alpha94511
8
<p>大家好,我是小白,书接上回,我们目前已经了解到了Python中很常见的一种数据类型:<strong><em>List</em></strong>,也初步掌握了一些Python自带的有关List的方法,今天为大家介绍另外一种异常火爆的数据结构:字典Dictionary,不夸张的讲,基本上我们熟悉了列表和字典后有关数据结构这里就差不多啦,实际的项目中应用的最多的也是这两种,至于其他的Set,Tuple平时用的不多~</p>
<h2>字典到底是什么</h2>
<p>简单来说,字典是另一种可变数据类型,且可存储任意类型对象。就是说字典里面存储的值我们可以修改~<br>字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中 ,格式如下所示:</p>
<pre><code>d = { #这里d的结构就是一个字典
<key>: <value>, #基础的数据结构就是一个key=>value
<key>: <value>,
.
.
.
<key>: <value>
}</code></pre>
<p>键必须是唯一的,但值则不必。值可以取任何数据类型,我会在接下来用详细的例子详细解释。<br>如果我们要把字典和列表做一个对比总结的话,可以发现如下特点:</p>
<ul>
<li>列表和字典的值都可以被修改</li>
<li>列表和字典都是动态结构,也就是说可以任意插入值,无限扩展</li>
<li>列表中获取值的方式是通过Index(索引)</li>
<li>字典中获取值是通过key(键)</li>
<li>列表字典都可以层次嵌套,比如一个列表中的值可以是另一个列表,</li>
</ul>
<p>好啦,让我们现在开始今天的栗子吧~</p>
<h2>Dictionary字典常用方法(内含扎心举例):</h2>
<blockquote><strong><em>创建一个字典</em></strong></blockquote>
<p>首先,不知道有没有小白朋友们感到疑惑,既然已经有列表了,为啥还需要字典这种数据结构 ?原因很简单哈哈,出于实际需要,我来举个令人伤心的例子说明:</p>
<p><strong><em>你是北京知名985,211毕业的北漂有为青年xiaobai,躲过了这轮互联网寒冬,23岁的你每天最爱穿着格子衫去街角咖啡厅点一杯可以无限续杯的伯爵红茶,顺便打量着周围45度仰脸拍抖音的小姐姐,意淫着一些不可告人的事情,这时来了一个青春无比的妹子,交谈甚欢后,同为北漂的她得知你技术入股进入一家创业公司,以及老板忽悠你的超级大饼和期权,内心狂喜,碰到潜力股了呀,程序猿呀,技术入股呀 !而你以多年老司机经验用余光扫描了妹子的三围,得出结论大概是82,62,82后,立刻邀请她去厕所做了一些羞羞的事情( 此处省略 6秒 ),几个月后迅速结婚然后生了一个丑萌的孩子...</em></strong></p>
<p>好啦,请你把这段经历用列表来表示一下 ? WTF! 答案几乎不可能,但是用字典的话某种程度上会解释的清楚一些~ 哈哈<br>如果用列表,就只能这么表示:</p>
<pre><code>young_man = ['xiaobai',985,23,'Developper'.......]</code></pre>
<p>完全不懂在说啥呀...但是如果用字典会舒服的多:</p>
<pre><code> young_man = {'first_name':'bai',
'family_name':'xiao',
'age':23,
'city':'Beijing',
'university_rank':'985',
'university_name':'beida',
'job':'Developer',
'salary':20000.00,
'company':'Start Up',
'married':True,
'wife_measurements':[82,62,82],
'number_of_children':1,
'future':None
}
</code></pre>
<p>瞬间感觉信息量暴涨有木有 ! 其实当我最开始接触字典的时候,我对它的第一感觉是经过select语句从数据库查询出的一个结果哈哈~, 好啦,让我们查看一下我们创建的这个字典:</p>
<pre><code>print(young_man)
Out: {'first_name': 'bai',
'family_name': 'xiao',
'age': 23,
'city': 'Beijing',
'university_rank': '985',
'university_name': 'beida',
'job': 'Developer',
'salary': 20000.00,
'company': 'Start Up',
'married': True,
'wife_measurements': [82, 62, 82],
'number_of_children': 1,
'future': None}</code></pre>
<p>大家可以发现输出的格式和创建的格式基本一样,都是符合上面我们提到过的key=>value 的形式,这里我们创建了一个字典并把字典的值赋给变量young_man,几点需要大家注意:</p>
<ul>
<li>字典的键是唯一的,类型也可以为int,我这个例子都是str类型,但是基本int类型的极少</li>
<li>在'university_name':'985' 这组信息中985是字符型,但是底下"number_of_children"的值为1,1是int型</li>
<li>字典的值可以是一个列表或者另一个列表,比如这里'wife_measurements’是指xiaobai老婆的三围,那可以传递一个包含三个值的列表,这也是我们上面说到过的列表字典都可以相互嵌套</li>
<li>字典的值可以是任意类型,比如这里的"future":None, 因为,因为你没有未来啊,啊哈哈</li>
</ul>
<blockquote><strong><em>访问字典里的值</em></strong></blockquote>
<p>现在我们已经成功创建了一个字典,那么如何获取字典中的值呢?我们知道在List中可以通过切片[start_index:end_index]来获取,字典里提供了另一种形式:</p>
<pre><code>dict[name_of_key] #把相应的键放入到方括号中</code></pre>
<p>现在让我们获得xiaobai的姓和工资:</p>
<pre><code> print("The first name of young man is :", young_man['first_name'])
print("Salary of young man:",young_man['salary'])
Out: The first name of young man is: bai
Salary of young man: 20000.00</code></pre>
<p>很简单吧,如果是xiaobai老婆的三围呢?</p>
<pre><code> print(young_man['wife_measurements'])
Out:[82, 62, 82]
</code></pre>
<p>如果就对胸围感兴趣呢? 因为'wife_measurements'这个键对应的值为一个列表,我们可以接着使用列表获取值的方法:</p>
<pre><code> print('The Bust measure is',young_man['wife_measurements'][0],'cm')
Out: The Bust measure is 82 cm
</code></pre>
<p>如果对所有的值都感兴趣呢?那很简单,字典提供了一个非常简单的方法values():</p>
<pre><code> print(young_man.values())
Out:dict_values(['bai', 'xiao', 23, 'Beijing', '985', 'beida', 'Developer', 10000, 'Start Up', True, [82, 62, 82], 1, None])
</code></pre>
<p>这样我们就获得young_man里面所有的值了</p>
<blockquote><strong><em>访问字典里的键</em></strong></blockquote>
<p>不用多解释,很简单,也是一个自带的方法.keys():</p>
<pre><code> print(young_man.keys())
Out: dict_keys(['first_name', 'family_name', 'age', 'city', 'university_rank', 'university_name', 'job', 'salary', 'company', 'married', 'wife_measurements', 'number_of_children', 'future'])
</code></pre>
<blockquote><strong><em>访问字典里的键和值</em></strong></blockquote>
<p>经过上面的介绍,我们可以分别获得字典的值和键,现在我们要把他们融合一下,其实很简单,因为本身字典可以迭代:</p>
<pre><code> for k,v in young_man.items():
print(k,":",v)
Out:first_name : bai
family_name : xiao
age : 23
city : Beijing
university_rank : 985
university_name : beida
job : Developer
salary : 10000
company : Start Up
married : True
wife_measurements : [82, 62, 82]
number_of_children : 1
future : None
</code></pre>
<p>字典里有一个方法:.items(),说白了就是获取到当前的键和值,这里我们用一个for循环可以迭代这个字典,分别输出键和值,这里注意一下,k,v是什么不重要,大家可以把k,v换成其他的字母都是没有问题的,有关字典的很多实用技巧我们下一期会讲。</p>
<blockquote><strong><em>添加,修改字典里的值</em></strong></blockquote>
<p><strong><em>结婚1年后的你攒了10万块,加上6个钱包的100万在北京付了首付,买来了小两居,然而丑萌的孩子,漂亮的老婆,加上房子的月供让你不堪重负,好在经过你的努力,现在的工资已经涨到了3万,变成了传说中的产品经理,身旁电视里响起共克时艰的号召,看似美好的生活向你招手,但坏消息突然接踵而至,首先第二个孩子的出生让你的经济状况雪上加霜,你在感叹杜蕾斯的产品质量同时突然发现事情有些不对......</em></strong></p>
<p><strong><em>你忽然回忆起很多细节,最近几个月赶项目,经常在深夜回家时发现一辆扎眼的保时捷正开出小区,到家时妻子经常满面潮红的刚刚洗完澡出现在你面前, 你经常关注着中兴老员工跳楼,xxx创业失败煤气自杀等负面新闻,却忽略了妻子钱包多出来的各种美容健身购物酒店卡....</em></strong></p>
<p><strong><em>眼前冒绿星的你心中一凉,拿起土著的照片看了看,又看看自己怀里新出生的老二,眉宇间竟有几分相似,脑袋嗡的一声后决定要去做亲子鉴定...</em></strong></p>
<p>首先先让我们看看如何在字典里新加一个值,让我们把xiaobai的银行存款和买加进去:</p>
<pre><code>young_man['bank_account']=100000
young_man['Own_house']=True
</code></pre>
<p>其实很简单啦,只要把ke和value按照图中形式添加就好了,这个时候我们再看young_man这个字典就会发现已经多了两个值:</p>
<pre><code>{'first_name': 'bai', 'family_name': 'xiao', 'age': 23, 'city': 'Beijing', 'university_rank': '985', 'university_name': 'beida', 'job': 'Developer', 'salary': 20000.0, 'company': 'Start Up', 'married': True, 'wife_measurements': [82, 62, 82], 'number_of_children': 1, 'future': None, 'bank_account': 100000, 'Own_house': True}
</code></pre>
<p>其次,xiaobai的职业,薪水,和孩子的数量有了变化,此时需要对字典中对应的值进行修改,这里十分简单:</p>
<pre><code>young_man['age']+=1
young_man['job'] = 'Product Manager'
young_man['salary']=30000.00
young_man['number_of_children']=2
</code></pre>
<p>再看一下现在的young_man:</p>
<pre><code>{'first_name': 'bai', 'family_name': 'xiao', 'age': 24, 'city': 'Beijing', 'university_rank': '985', 'university_name': 'beida', 'job': 'Product Manager', 'salary': 30000.0, 'company': 'Start Up', 'married': True, 'wife_measurements': [82, 62, 82], 'number_of_children': 2, 'future': None, 'bank_account': 100000, 'Own_house': True}
</code></pre>
<p>此外如果修改多个值,用update()是最方便的,我们下一篇笔记会说~</p>
<blockquote><strong><em>删除字典里的值</em></strong></blockquote>
<p><strong><em>医院的结果出来了,不出意料,杜蕾斯质量没问题,你迅速回到家中准备兴师问罪,却发现妻子早已准备好了离婚协议,在暴怒之中的你想也没想就选择了净身出户,把所有的一切都留给了妻子,自己没日没夜地工作期待有一天公司上市自己套现离场,继续开始人生下半场,然而一切都是命中注定,不知怎么,你也成为了《就算老公一毛钱股份都没拿到,在我心里,他依然是最牛逼的创业者》里的主角,命运向你展示了它狰狞的一面,你最后的希望随之破灭,终日借酒浇愁。</em></strong></p>
<p><strong><em>在一个百无聊赖的晚上,你带着仅有的几千块钱做了一次高级大保健,负责给你服务的小姐姐名叫skyler,来自美国新墨西哥州的Albuquerque市,人生失意的你唱着崔健的<<一无所有>>和她倾诉了自己的前半生,笑靥如花的小姐姐安慰了你,向你描述了美利坚大地上美好的一切和自己同样悲惨的前半生,同时天涯沦落人,相逢何必曾相识!</em></strong></p>
<p><strong><em>伴随着大保健配套的bgm《沙漠骆驼》,醉眼朦胧的你仿佛看到了遥远的Albuquerque市,便宜的大别墅和skyler的笑脸,一切尽在咫尺,却又遥不可及。。。</em></strong></p>
<p>回到正题,这里xiaobai选择净身出户,因此很多属于他的东西全部没了,删除字典中的值很简单,只需要del关键字:</p>
<p><img src="/img/bVbnPCd?w=1165&h=112" alt="clipboard.png" title="clipboard.png"></p>
<p>这里删除的东西有点多,我利用了上篇笔记讲过的知识,首先建立了一个列表,装入young_man的想要删除的键值,然后利用迭代一次删除,这并不是一个优雅的方法,但是目的是让大家看一下列表和字典的一个结合操作,现在再看一下young_man:</p>
<pre><code>{'first_name': 'bai',
'family_name': 'xiao',
'age': 24,
'city': 'Beijing',
'university_rank': '985',
'university_name': 'beida',
'future': None}
</code></pre>
<p>这个时候如果我们还想看看银行存款是不可能的了,如果我们尝试老方法:</p>
<pre><code>young_man['bank_account'] # ERROR</code></pre>
<p>会很容易的报错,因为在字典中已经没有这个值了,为了避免报错,我推荐大家使用get方法:</p>
<pre><code>print(young_man.get('bank_account','Not Found')) # 如果没有输出Not Found
Out: Not Found</code></pre>
<blockquote>
<strong>创建字典的其他方式</strong>*</blockquote>
<p><strong><em>还未到30的你做出了一个人生中最重要的决定,那就是和skyler一起去她的老家Albuquerque市,从零开始</em></strong><br><strong><em>你用你仅存的化学知识在当地的高中谋取到了一个化学老师的工作,利用自己姓名xiaobai起了一个地道的美国名字:Walter White, 小城市的生活悠闲惬意,当地的炸鸡店“Los Pollos Hermanos”味美绝伦,你和skyler也有了爱的结晶,又一个丑萌而且先天残疾的孩子出生了。。</em></strong></p>
<p>此时的xiaobai已经有了全新的身份,这里我们换一种方式创建他的全新个人信息:</p>
<pre><code>walter_white_keys = ['name','age','job','subject','adress','wife']
walter_white_values = ['Walter White',50,'Teacher','Chemistry','Albuquerque','Skyler']
Breaking_Bad = dict(zip(walter_white_keys,walter_white_values))
</code></pre>
<p>查看一下全新的老白:</p>
<pre><code> print(Breaking_Bad)
{'name': 'Walter White',
'age': 50,
'job': 'Teacher',
'subject': 'Chemistry',
'adress': 'Albuquerque',
'wife': 'Skyler'}
</code></pre>
<blockquote>
<strong>清空字典</strong>*</blockquote>
<p><strong><em>你也会时常想起国内抛下的孩子,感慨天道有轮回,善恶到头终有报,20年如白驹过隙, 稍纵即逝,此刻你已年近50,知天命的年龄被诊断出了癌症,正当你觉得人生无望的时候,一个曾经教过的小混混学生Jesse Pinkman出现在你的面前,从此江湖上开始了你的传说。。。</em></strong></p>
<p><strong><em>故事的最后,你看着腹部缓缓流出的鲜血,没有理会,依然专注地在制作眼前令无数人痴迷的蓝色结晶物,直到体力不支缓缓倒地,在生命的最后时刻,伴随着耳边的《Baby Blue》,眼前依稀看到了30年的那个意气风发的少年坐在街边咖啡馆,正专注地学习Python,直到一个女孩款款向他走来。。。。。。</em></strong></p>
<p>至此,一代枭雄终落幕,最后让我们忍住眼泪,敲下最后的代码:</p>
<pre><code> Breaking_Bad.clear()
del Breaking_Bad</code></pre>
<p>我想可能有一句话能概括最后时刻老白的内心想法:我爱你不后悔,也尊重故事的结尾</p>
<p><img src="/img/bVbnPCo?w=780&h=437" alt="clipboard.png" title="clipboard.png"></p>
<p>如果大家对Dict的其他常见使用方法感兴趣,可以关注我的微信公众号Python极简教程,我会把最高效,简洁的小技巧一一记录下来,分享给大家:</p>
<p><img src="/img/bVbwxMn?w=258&h=258" alt="图片描述" title="图片描述"></p>
Python 基础起步 (六) List的实用技巧大全
https://segmentfault.com/a/1190000018016421
2019-01-25T07:04:37+08:00
2019-01-25T07:04:37+08:00
alpha94511
https://segmentfault.com/u/alpha94511
6
<p>## List初步进阶 ##</p>
<p>hello,大家好,经过上篇笔记的介绍,我们已经对List这种数据类型有了初步的理解,今天我要趁热打铁,为大家介绍一些实用的List技巧,希望能帮助到各位大家~</p>
<p><strong><em>extend合并列表()</em></strong></p>
<pre><code> first_lst = ['I','am','noob']
second_lst = [12,34,56]
first_lst.extend(second_lst)
print(first_lst)
Out:['I', 'am', 'noob', 12, 34, 56]</code></pre>
<p>简单来说List1.extend(List2),会返回List1,结果是将List2添加到List1里,相当于extend前面的列表合并括号里的。</p>
<p><strong><em>count()查看列表中元素出现次数</em></strong></p>
<pre><code> lst = [1,2,3,2,4,5,5,5,6,7]
print(lst.count(5))
Out: 4
</code></pre>
<p>这个方法很简单但是却很实用,会知道一个元素在列表中出现的次数,这里5出现了3次,结果输出3</p>
<p><strong><em>分解列表赋值</em></strong></p>
<pre><code>a = [1, 2, 3]
x, y, z = a
print(x)
print(y)
print(z)
Out:1
2
3</code></pre>
<p>这里很有意思,简单来说就是我们可以分别把一个列表中的值分别赋值给变量</p>
<p><strong><em>List.index()</em></strong></p>
<pre><code> lst = ['I','am','noob']
lst.index('am')
Out:1
lst.index('good')
Out:ValueError: 'adfa' is not in list</code></pre>
<p>我们可以获取列表中一个值的index,但是如果列表中不存在这个值会抛出ValueError</p>
<p><strong><em>sorted(List,reverse=True or False)</em></strong></p>
<pre><code> numbers = [2,1,3,5,4,8,6,7]
ascending = sorted(numbers)
descending = sorted(numbers,reverse=True)
print(ascending)
print(descending)
Out:[1, 2, 3, 4, 5, 6, 7, 8]
[8, 7, 6, 5, 4, 3, 2, 1]</code></pre>
<p>sorted()括号里面可以放入一个可排序的list,默认reverse=False,也就是从小到大啦,如果我们赋值reverse=True,那就是倒序啦,大家可以试试字符串在列表里是什么情况~</p>
<p><strong><em>List.insert(index,value)</em></strong></p>
<pre><code> numbers = [1,3,5,7,9]
numbers.insert(0,0)
print(numbers)
Out:[0, 1, 3, 5, 7, 9]</code></pre>
<p>这个方法很好理解对不对!就是向一个列表里面插入值,括号里面第一个值是索引,第二个值是想要插入的值</p>
<p><strong><em>倒序输出一个List</em></strong></p>
<pre><code> numbers = [1,3,5,7,9]
reverse_numbers = numbers[::-1]
print(reverse_numbers)
Out:[9, 7, 5, 3, 1]
</code></pre>
<p>这里可能知识点有点略微超前,利用List的切片功能,这里numbers后面的中括号其实包括默认的三个值:</p>
<ul><li>[start_index : end_index : steps]</li></ul>
<p>最后的steps意思就是说隔几个值选取,这里我们全选numbers里所有的值,但是-1就是倒序一个个输出啦。如果还有不明白的小白朋友们可以百度一下哈,嗖的一下百家号Python补习班就出来啦,哈哈,你啥都没查到~ 开个小玩笑。</p>
<p><strong><em>filter,map,lamba ,reduce</em></strong></p>
<p>关于这四个方法的具体讲解就不在这里啦,因为我们是小白,对目前来说有点不好理解,之后我会专门讲一下,大家可以看看例子:</p>
<ul>
<li><strong><em>filter(function, sequence):对sequence中的item依次执行function(item),将执行结果为True的item组成一个List/String/Tuple(取决于sequence的类型)</em></strong></li>
<li><strong><em>map(function, sequence) :对sequence中的item依次执行function(item),见执行结果组成一个List返回</em></strong></li>
<li><strong><em>reduce(function, sequence, starting_value):对sequence中的item顺序迭代调用function,如果有starting_value,还可以作为初始值调用,例如可以用来对List求和</em></strong></li>
<li>lambda:这是Python支持一种有趣的语法,它允许你快速定义单行的最小函数</li>
</ul>
<p>现在依次举栗子啦:</p>
<p><strong><em>filter()根据返回值True还是False 筛选奇偶数</em></strong></p>
<pre><code> numbers= [1,2,3,4,5,6,7,8,9,10]
even_numbers =list(filter(lambda x:x % 2,numbers))
odd_numbers = list(filter(lambda x:x % 2==0,numbers))
print('Even Numbers are :',even_numbers)
print('Odd Numbers are :',odd_numbers)
Out:Even Numbers are : [1, 3, 5, 7, 9]
Odd Numbers are : [2, 4, 6, 8, 10]
</code></pre>
<p><strong><em>map()根据一个数字列表生成一个每个值都是原来3倍的数组</em></strong></p>
<pre><code> numbers= [1,2,3,4,5,6,7,8,9,10]
triple_numbers= list(map(lambda x:x*3,numbers))
print('Triple Numbers are :',triple_numbers)
Out:Triple Numbers are : [3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
</code></pre>
<p><strong><em>reduce()根据一个数字列表生成累积和</em></strong></p>
<pre><code> from functools import reduce
numbers= [1,2,3,4,5,6,7,8,9,10]
result_add= reduce(lambda x,y:x+y,numbers)
print('Total :',result_add)
Out: Total : 55</code></pre>
<p>最后这几个不需要大家现在就搞明白,前几个可以熟悉一下,最好能自己练习一下,如果大家对List的其他使用技巧感兴趣,可以关注我的微信公众号: Python极简教程,我会把最高效,简洁的小技巧一一记录下来,分享给大家:</p>
<p><img src="/img/bVbwxMn?w=258&h=258" alt="图片描述" title="图片描述"></p>
Python 基础起步 (五) 一定要知道的数据类型:初识List
https://segmentfault.com/a/1190000018002749
2019-01-24T05:18:25+08:00
2019-01-24T05:18:25+08:00
alpha94511
https://segmentfault.com/u/alpha94511
8
<h2>什么是List</h2>
<p>Hello,小白同胞们,我回来啦,前面的学习中,我们已经知道了两种python的数据类型:int和str,今天将要给大家接着介绍Python中一种非常常见,非常有用的数据类型,那就是List(列表),在Python中用中括号表示[]</p>
<p>那这个List到底是什么东西呢,其实说白了就是一组有序的数据集合,怎么理解呢,最直观的理解就是像个盒子,我们可以把整数啊,字符串啊,还有其他很多类型的数据统统放在里面,但是要注意,它是有顺序的。</p>
<h2>举个栗子</h2>
<p>让我们举个栗子,上篇笔记我们学习了变量,先复习一下:</p>
<pre><code>a = 1
b = 'xiaobai'
c = 23.67
d = Ture</code></pre>
<p>这里我声明了4个变量a,b,c,d 分别给它们赋值为整数,字符串,浮点数,布尔型,然后我可以把它们直接装入一个List中:</p>
<pre><code>lst = [a,b,c,d]
print(lst) # 输出 [1,'xiaobai',23.67,True]</code></pre>
<p>很简单吧,这样我们就新建了一个List,然后把新建的列表赋给变量lst,注意这里是有顺序的,因为我是依次将a,b,c,d放入其中</p>
<h2>创建一个List</h2>
<p>看过了上面的栗子,让我们直接创建几个列表吧:</p>
<pre><code>first_list = [] #新建一个空List, 因为里面什么都没有
second_list = [1,2,3,4,'love'] # 各种类型的数据都可以往里扔
third_list = [ True,False,12.333,'My name is xiao bai'] # 同上</code></pre>
<p>让我们查看一下刚刚创建好的三个列表:</p>
<pre><code>print(first_list )
[]
print(second_list)
[1, 2, 3, 4, 'love']
print(third_list)
[True, False, 12.333, 'My name is xiao bai']</code></pre>
<p>其实除了这些,列表的创建方式还有很多,可以利用list()通过转化其他数据结构而来,也可以创建多个列表在一个列表中实现嵌套等等,之后我会在讲完Python基础起步之后专门开一个Python小白进价的专栏,会详细讲到List的使用技巧,但是目前,知道这些足够啦</p>
<h2>List基础方法,添加值,修改,删除值</h2>
<p>让我们从添加值到列表开始,首先让我们新建一个空列表:</p>
<pre><code> lst = [] # 新建一个空列表
print("Intial empty List: ",lst)
Out:[]</code></pre>
<p>利用列表自带的append方法可以直接依次为空列表添加值:</p>
<pre><code> lst.append(1)
lst.append('paris')
lst.append(True)
print(lst)
Out:[1,'paris',True]</code></pre>
<p>这样我们就依次把1,'paris',True 三个值加进列表lst里面了,那如果我们要是想查看列表中的一个值应该怎么办呢,见下图:</p>
<p><img src="/img/bVbnHuY?w=778&h=301" alt="clipboard.png" title="clipboard.png"></p>
<p>就像我刚才特意强调的,列表是有顺序的,表示索引的东西叫做Index,它是从0开始的,所以如果拿我们的例子来说,1 对应的索引为0,'paris'对应的索引为1,True 对应的索引为2,要查看列表中的一个值可以直接用这种新式:List[Index],比如:</p>
<pre><code>lst[0] # 1
lst[1] # 'paris'
lst[2] # True
lst[:] # [1, 'paris', True]
lst[0:2] # [1, 'paris']
lst[-1] # True</code></pre>
<p>如果索引是单独的一个数字,会直接输入对应的值,但是索引还可以用切片的形式表示:</p>
<pre><code>List[起始Index :结束Index]
</code></pre>
<p>这里要注意一点,它不包括最后结束的Index,所以当我使用lst[0:2]查看时,它只输出对应索引为0和1的值。<br>好啦,那如果我现在想要改变List中一个值也很简单,因为我们了解了索引,便能很方便的定位元素啦,如果我要修改1和Ture的值为 'My','Life' :</p>
<pre><code> lst[0]='My'
lst[2]='Life'
print(lst)
Out: ['My', 'paris', 'Life']</code></pre>
<p>如果是删除的话可以用List的自带函数叫remove() 或者pop()</p>
<pre><code> lst.remove("My")
print(lst)
Out:['paris', 'Life']
</code></pre>
<p>Remove 注意两点:</p>
<ul>
<li>如果正确删除,不会有任何反馈。没有消息就是好消息</li>
<li>如果所删除的内容不在list中,就报错。注意阅读报错信息:x not in list</li>
</ul>
<p>如果是pop呢?</p>
<pre><code> lst.pop()
Out: 'Life'
print(lst)
Out: ['paris']
</code></pre>
<p>pop(index)如果括号里没有任何东西,会默认删除List里面最后一个值,如果pop(2)的话会删除List里面索引为2的值,而且这个方法很有意思,它会自动返回被删除的值,用人话说就是,你删除List里面一个值,还得到了它。。。</p>
<h2>List总结</h2>
<ul>
<li>列表可以是无限大,包含任意类型的元素</li>
<li>列表中有很多强大的自带方法,比如len(List)可以获得一个列表的长度,还有sort(),filter(),reduce(),map()等等,大家可以自己谷歌一下查查</li>
<li>相对来说,列表在性能上普遍不佳,因为有序,所以带索引,拖慢了一定速度,尤其是一个特别大的列表在运行查询相关的操作的时候,我们之后会讲</li>
</ul>
<p>完结,撒花~</p>
Python 基础起步 (四) 变量是什么东西 ?
https://segmentfault.com/a/1190000017987549
2019-01-23T04:59:18+08:00
2019-01-23T04:59:18+08:00
alpha94511
https://segmentfault.com/u/alpha94511
6
<h2>变量是什么</h2>
<p>Hello, 大家好,我回来啦,今天想为大家介绍Python里面一个神奇的东西:变量。其实这个东西真正要非常详细的解释的话要将好多,因为它至少包含以下几种类型:</p>
<ul>
<li>全局变量 (在模块内、在所有函数外面、在class外面是全局变量)</li>
<li>局部变量 (在函数内、在class的方法内,未加self修饰就是局部变量)</li>
<li>静态变量 (在class内的,但不在class的方法内的,这就是静态变量)</li>
<li>实例变量 (在class的方法内的,用self修饰的变量,这就是实例变量)</li>
</ul>
<p>大家不用担心,我们目前只会用到全局变量,其他的不用管啦,之后的会在介绍完函数,方法,类的时候提到,言归正传,所谓变量无非就是代表一段信息的名字而已,比如:</p>
<pre><code>a='Hello World'
b=123
c=True
d= False</code></pre>
<p>在这个例子里面,a,b,c,d 就是四个变量,他们分别代表了等号右边的值,一个变量可以是几乎任何值,在这里想必给位小白朋友们也会发现,变量a的值是上篇笔记提到的字符串类型str,b是整型int,c,d是布尔类型,总之大家只要记住变量几乎可以代表任何值就对啦,之后我们学习列表,字典的时候还会提到。好啦,回到主题,因为一个变量代表着它等号右边的东西,那么如果我们想要获得一个变量的值也很容易,如下图:</p>
<p><img src="/img/bVbnDxO?w=679&h=327" alt="输出变量" title="输出变量"></p>
<p>这里我新建了四个变量,分别叫name,age,country,和city,大家可以感觉到其实就是一个人的基础信息,那我们如果想要查看变量的值在notebook 里是非常容易的,可以print(变量名字)或者更简单的直接在一个模快里写下变量的名字后直接Shift+Enter 运行</p>
<p>现在我们已经知道如何创建一个变量啦,其实创建变量的方式还有很多,比如:<br><img src="/img/bVbnDxR?w=865&h=392" alt="clipboard.png" title="clipboard.png"></p>
<p>我们可以像图片中的这样,同时给多个变量赋值,也可以新建一个变量,使其等于另一个变量的值,比如d=c就是这个意思,其实我个人还是觉得非常好理解的,不是吗~ 就是连等而已。</p>
<p>现在让我们再关注一下变量的另外一个特性,它的值可以被改变,我说的是目前我们用到的变量,之后有其他限制会再说,大家目前只要记住可以随时改变变量的值就够啦,比如:</p>
<pre><code>a='I love paris'
a=1
print(a) # 输出 1</code></pre>
<p>这里我们可以看到,我们第一次新建变量a,使它的值等于一个字符串,有紧接着对它进行了重新赋值,那么最终结果也就改变了,总是以最新的为准。</p>
<p>最后说一下关于变量的删除,目前如果大家练习可以不用删除任何变量,因为还没到那个地步,但是有可能以后会涉及到,比如我举个自己的例子,由于做数据分析,经常会把一个数据结构(ex.Pandas Frame)赋值给一个变量,那这种情况下一个变量会很大,占用大量的内存,出于运行性能的考虑,会定时删除掉不用的变量,好像扯远了,哈哈,删除变量其实超级简单,只要一行代码的:</p>
<pre><code>name = 'xiaobai' # 新建变量
del name # 删除变量</code></pre>
<p>这里del其实就是delete的缩写,很容易理解,最后我想说有关于变量的命名其实十分重要,这里给大家几点建议:</p>
<ol>
<li>变量名字具有一定的含义。比如写:n = "xiaobai",就不如写:name = "qiwsir"更好。</li>
<li>名字不要误导别人, 比如name=18,这就是非常错误的写法,因为大家觉得这个变量后面是一个名字,应该是字符串类型,而不是整数</li>
<li>名字要有意义的区分,有时候你可能会用到a1,a2之类的名字,最好不要这么做,换个别的方式,通过字面能够看出一定的区分来更好。</li>
<li>最好是名称能够读出来,千万别自己造英文单词</li>
</ol>
<p>除了这四个有关命名的建议,还有一些禁忌也要注意呀:</p>
<ol>
<li>区分大小写</li>
<li>禁止使用保留字,所谓保留字不知道大家还记不记得,比如print就是一个Python自带的关键字,那么你在命名的时候千万不要新建一个变量也叫print,类似的特殊字一共也不太多,还有像我们见过的type,list,dict等</li>
</ol>
<p>如果大家实在无聊,非常感兴趣看看Python有多少保留字,可以直接在notebook里运行以下代码:</p>
<pre><code>import keyword
print(keyword.kwlist)</code></pre>
<p><img src="/img/bVbnDxT?w=594&h=730" alt="clipboard.png" title="clipboard.png"></p>
<p>这里其实是引用了一个Python自带的包keyword,然后调用了一个方法而已,这些都不用担心,我之后会陆续给大家讲到,哈哈!这期就写到这里啦,今天巴黎初雪,祝大家也瑞雪兆丰年,用一张超美的雪景结束,撒花~</p>
<p><img src="/img/bVbnDxU?w=1084&h=1080" alt="图片描述" title="图片描述"></p>
Python 基础起步 (三) 基础的数据类型,简单运算
https://segmentfault.com/a/1190000017973576
2019-01-22T04:14:29+08:00
2019-01-22T04:14:29+08:00
alpha94511
https://segmentfault.com/u/alpha94511
6
<h2>基础数据类型</h2>
<p>大家好,经过上篇文章的讲解,相信同为小白的你已经熟悉了Jupyter Notebook 的基础操作,在此为大家总结一些常用的快捷键,能够方便大家使用:</p>
<ul>
<li>在选中模块下方添加一个新的模块: b</li>
<li>在选中模块上方添加一个新的模块: a</li>
<li>删除选中的模块: x</li>
<li>为选中内容添加注释</li>
<li>运行当前模块:Shift + Enter</li>
<li>调出快捷键菜单:h</li>
</ul>
<p>还有就是如果大家需要从头到尾运行一个ipynb文件的话,可以在上方的菜单选择Kernel后单击Restart & RunAll 即可,如下图:</p>
<p><img src="/img/bVbnzUc?w=996&h=467" alt="运行整个Notebook" title="运行整个Notebook"></p>
<p>大家如果感兴趣也可以自行Google或者百度一下Jupyter Notebook 的一些只用技巧,总之如果你和我一样是个小白一样的话,我强烈大家用这个来写Python,千万不要被其他教程诱惑,去自己下载Pyhton和一堆其他的编辑器啥的,最后搞出一堆问题,到头来给自己徒增烦恼。</p>
<p>好啦,现在让我们进入主题,今天我主要想给大家说一下Python里的数据类型,用小白的话说就是Python能显示或者使用什么类型的信息,是数字,字符串,分数小数等等。</p>
<p>在这里我继续使用上篇笔记中使用过的hello_python.ipynb文件</p>
<p><img src="/img/bVbnzUi?w=880&h=276" alt="新增加了两行" title="新增加了两行"></p>
<p>大家可以看到我在上次的Hello World 后添加了两行,还是利用print打印出一个字符串“abc” 和数字123,所以毫无疑问,现在你知道Python支持所有数字和文本类的信息,具体一点的话这两个数据类型分别为字符串(String)和整型(int), 划重点啦,现在给大家科普一个Python自带的好东西,叫做 type(object), 顾名思义,就是要显示一个东西的类型,我们可以想要查看的东西的类型放到括号里,是object</p>
<p>举个栗子,如果在一个模块里输入如下代码,然后Shift+Enter运行当前模块,我们会发现输出如下信息:</p>
<pre><code>type("abc")
str</code></pre>
<p><img src="/img/bVbnzUl?w=992&h=384" alt="输入四种类型的数据类型" title="输入四种类型的数据类型"></p>
<p>在这里,我分别查看了四种数据类型,分别是字符串(str),整数(int),浮点数(float),布尔类型(bool),用人话说就是文字,数字,带小数点的数字,布尔类型只有两种值(True,False),不像其他的教程会在这里给你无限拓展,但我说真心话,对于很多人来说,知道这四种类型足够了,小小总结一下:</p>
<ul>
<li>str (字符串) 一般用"" 或者‘’ 括起来</li>
<li>int (整数) 比如 123,5, 67等</li>
<li>float (浮点型)比如0.4, 4.78.5.0 等</li>
<li>bool(布尔型),不是True就是False,之后会细说,很多时候就是为了判断,简单吧~</li>
</ul>
<p>好啦,现在我们已经知道Python使用频率非常高的四种数据类型,现在我们可以看看Python到底能干啥,第一个最智障的功能就是计算了,谁让我们用的是计算机呢哈哈,如果一个编程语言都不能进行正常的数学运算还是别出来丢人啦,这里我们简单看看Python的数学四则运算:</p>
<p><img src="/img/bVbnzUq?w=766&h=503" alt="简单的四则运算" title="简单的四则运算"></p>
<p>哈哈,怎么样,看完这个简单的四则运算是不是觉得超级简单呀,还有就是python其实自带了很多很有用的方法和函数可以帮助大家进行其他复杂一点的运算,比如:</p>
<p><img src="/img/bVbnzUs?w=651&h=289" alt="一些其他的运算方法" title="一些其他的运算方法"></p>
<p>温馨提示大家,这些东西都不用记哈,真正实际使用的时候再查就好啦,仅仅知道四则运算就足够了,之后我为大家介绍一些其他知识点时也会再次提醒你们的,非常多的东西略懂皮毛,或者不懂也真的不影响,希望你们能相信我,作为文科生的小白朋友们~</p>
<p>这次就为大家介绍到这里,还是老样子,为大家做一个小总结:</p>
<ul>
<li>常用的数据类型就是字符串,整形,浮点型,布尔</li>
<li>数学计算只要看得懂四则运算就够了</li>
<li>不用担心大整数问题,除不尽什么的问题,python会自动处理</li>
<li>type(object)是个炒鸡棒的方法,一定牢记哦,之后学习会反复用到!!</li>
</ul>
<p>就这样,祝大家学习开心,天天愉快,完结,撒花~</p>
Python 基础起步 (二) 5分钟内下载环境并运行第一个Python 程序
https://segmentfault.com/a/1190000017960570
2019-01-21T04:02:58+08:00
2019-01-21T04:02:58+08:00
alpha94511
https://segmentfault.com/u/alpha94511
4
<h2>下载并安装Anaconda</h2>
<p>好啦,作为小白,我们的第一步是安装能够运行Python的环境,所谓环境就是指我们要安装一个软件,之后就能用它来写Python代码 (觉得我对环境解释特别智障的请绕行,我们是小白!!!)</p>
<p>想我当初在安装环境时被网上安利的五花八门,为了让大家避免这种烦恼,统一听我口号:所有人,都过来,鼠标在手,跟我走!</p>
<ul>
<li>下载 Anaconda <a href="https://link.segmentfault.com/?enc=rj9FeZYKDlvXNVT%2B9rXMVw%3D%3D.9pBD7Htag6bsdKA%2Fv711LjNsEcE01XxYFVf2QxAnAZY%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=bxhGmTgFhW9Zcwol%2FZzm0w%3D%3D.XQiVDRGyfGwIUkAvcOJERo2IBl67yFDkKObs0kaNm0vB%2FsbdAroGUjBaCWjwE3eQ" rel="nofollow">https://www.anaconda.com/down...</a>
</li>
<li>一路默认安装即可</li>
</ul>
<p>在打开上述链接后,我们会看到如下界面:<br><img src="/img/bVbnuWZ?w=1052&h=891" alt="网站会自动识别你当前的操作系统" title="网站会自动识别你当前的操作系统"></p>
<p>这里不论用的是windows或是mac,单击Download大按钮就会自动下载了(安装文件有点大,我说的5分钟不包括下载时间,哈哈),如果你是Liux可以离开了,因为你不是小白,请照照镜子确认一下自己的身份。下载完成后即可双击运行安装程序,为了避免麻烦,一路默认安装即可,这里我偷点懒,截了一些图展示过程~,大家如果把鼠标悬停在图片上可以看到我的详细解释~</p>
<p><img src="/img/bVbnuXy?w=774&h=595" alt="双击exe文件后开始安装" title="双击exe文件后开始安装"></p>
<p><img src="/img/bVbnuXC?w=762&h=599" alt="选择Justme即可" title="选择Justme即可"></p>
<p><img src="/img/bVbnuXD?w=760&h=581" alt="一路默认安装至C盘即可" title="一路默认安装至C盘即可"></p>
<p><img src="/img/bVbnuXE?w=764&h=593" alt="两个都勾上,不要问我为什么" title="两个都勾上,不要问我为什么"></p>
<p><img src="/img/bVbnuXF?w=762&h=596" alt="安装过程会花点时间,如果出现突然跳出的黑色小窗口不用担心,一切正常" title="安装过程会花点时间,如果出现突然跳出的黑色小窗口不用担心,一切正常"></p>
<p><img src="/img/bVbnuXH?w=768&h=594" alt="大功告成,最后这个可以不勾选" title="大功告成,最后这个可以不勾选"></p>
<p>好啦,至此安装结束,我们验证一下是否安装成功,只需要点击开始菜单,会在A开头的目录下找到Anaconda的名字,如下面这张图片所示</p>
<p><img src="/img/bVbnuXJ?w=324&h=404" alt="目录" title="目录"></p>
<p>我们会发现在Anaconda目录下有五个东西:</p>
<ol>
<li>Anaconda Navigator</li>
<li>Anaconda Prompt</li>
<li>Jupyter Notebook</li>
<li>Reset Spyder Settings</li>
<li>Spyder*</li>
</ol>
<p>重点来啦,我们只用一个,那就是Jupyter Notebook,Jupyter Notebook,Jupyter Notebook 重要的事情说3遍,其他的四个目前不需要知道干什么的,因为我们是小白。知道太多会消化不良的~</p>
<p><img src="/img/bVbnuXL?w=334&h=276" alt="单击打开Jupyter Notebook" title="单击打开Jupyter Notebook"></p>
<p>如图所示,单击Jupyter Notebook ,神奇的事情发生了,我们会自动在默认的浏览器打开一个新的界面,长这个样子,我们发现网址为:<a href="https://link.segmentfault.com/?enc=hT6RlpvcIWOBgGv9p1r%2BfA%3D%3D.ygzHmohJJGKmrkQK2LguYM6%2BCx08xSudQBmVtane9m0%3D" rel="nofollow"></a><a href="https://link.segmentfault.com/?enc=n4B3CDSOQ79mMlVbXWftwQ%3D%3D.lrceNX3OQp55iGRsQ8r4wZBU8scSDCT5vOnLfsYcOqU%3D" rel="nofollow">http://localhost</a>:8888/tree,不用知道为什么,我们不用知道关心这些没用的信息。下图就是可以写Python代码的主界面了,激不激动,开不开心!</p>
<p><img src="/img/bVbnuXM?w=1763&h=863" alt="Notebook 主界面" title="Notebook 主界面"></p>
<h2>开启Python奇幻之旅</h2>
<p>首先,我们不需要关心上面这张图片里乱七八糟的文件夹,只需要关心右上角有一个 New 的按钮,就是新建啦,每次写Python的时候我们要新建一个文件,把代码写在里面,和写word原理一样,单击New会弹出下拉菜单,我们选择Python 3 ,于是我们发现浏览器又新打开了一个页面</p>
<p><img src="/img/bVbnuXN?w=590&h=336" alt="选择Python3" title="选择Python3"></p>
<p><img src="/img/bVbnuXO?w=1148&h=448" alt="新建的Python文件" title="新建的Python文件"></p>
<p>我们发现在页面左上角 Jupyter的logo旁边有一个 Untitled的字样,也就是说我们还没有给新建的Python文件起名字,单击 Untitled 会弹出修改名字的界面,这里我命名为hello_python作为文件的名字,单击rename,我们就会发现文件的名字已经改过来啦!</p>
<p><img src="/img/bVbnuXR?w=1264&h=421" alt="重命名" title="重命名"></p>
<p><img src="/img/bVbnuXV?w=762&h=307" alt="修改成功" title="修改成功"></p>
<p>现在让我们完成人生中的第一个Python程序,在可以输入的区块内输入:</p>
<pre><code>print("Hello World")</code></pre>
<p>然后单击区块上方Run的按钮,立刻就可以发现Hello Worl的已经输出在下方啦! 这就已经是一个Python 程序了,我的小白同胞们!!</p>
<p><img src="/img/bVbnuX3?w=816&h=386" alt="单击run进行运行后" title="单击run进行运行后"></p>
<p>简单给大家介绍一下这个Notebook的界面基本功能,在一个一个区块里面我们可以书写Python代码,在我们输入代码时区块会变成绿色,之后如果选中区块会变成蓝色,单击Run会运行当前区块的内容,刚才我们写的代码就是输出一个字符串,print("") 引号里面可以加入任何想要输出的内容,具体的细节之后会说</p>
<p><img src="/img/bVbnuX5?w=847&h=246" alt="区块选中状态" title="区块选中状态"></p>
<p><img src="/img/bVbnuX6?w=880&h=231" alt="区块输入状态" title="区块输入状态"></p>
<p>在我们写完代码后可以按下熟悉的 Ctrl+s 进行保存,之后关闭当前页面,我们就会发现在主页的下方出现了我们刚刚保存好的文件hello_python.ipynb ,至于为什么这个格式,有兴趣的朋友可以去查一查jupyter Notebook的相关用法,不过你要是和我一样关心效率,就不用管它了,你知道能我们用Jupyter Notebook 可以写代码运行Python就好了~</p>
<p><img src="/img/bVbnuYd?w=1514&h=580" alt="回到主页后,发现刚刚的文件已经自动保存啦" title="回到主页后,发现刚刚的文件已经自动保存啦"></p>
<p>接下来我们只要关闭Jupyter Notebook的这个主页页面就可以退出啦,别忘了后台还有一个控制台窗口也要关闭一下,下次再进入方法一样</p>
<p><img src="/img/bVbnuYl?w=864&h=456" alt="别忘了关闭后台窗口" title="别忘了关闭后台窗口"></p>
<h2>总结</h2>
<p>最后总结几点:</p>
<ol>
<li>下载Anaconda选择3.7 安装一路默认即可,选择Just me</li>
<li>我们目前只用Jupyter Notebook,用它可以方便的写Python代码并运行</li>
<li>如果看到网上其他关于环境安装什么pip啊,什么visual stuido code,PyCharme,Sublimtext之类的,直接无视就好,相信我这个小白的选择</li>
</ol>
<p>撒花,完结!!</p>
Python 基础起步(一)写在开篇的话,写给同为小白的你
https://segmentfault.com/a/1190000017954410
2019-01-20T02:47:14+08:00
2019-01-20T02:47:14+08:00
alpha94511
https://segmentfault.com/u/alpha94511
7
<h2><strong>一个小白的成长日记</strong></h2>
<p>大家好,这是我在饭否的第一篇笔记,先简单介绍一下自己,我于2016年在北京工业大学本科毕业,同年9月来到法国巴黎Efrei工程师学校读研,在2018年11月毕业后加入了法国兴业银行(Société Générale)担任数据分析师的职位,主要负责数据质量监测,分析,潜在风险预测,前端可视化报表制作及Sharepoint开发等。<br><img src="/img/bVbnuVe" alt="clipboard.png" title="clipboard.png"></p>
<pre><code> 北京CBD同款写字楼... </code></pre>
<p>工作需要使用Python做自动化开发及数据分析,我作为完全0基础的小白,成功忽悠HR通过面试后,深感会立刻露馅,于是一边工作一边在网上开始找教程进行学习,学习过程痛苦不堪,其中主要存在三点问题:</p>
<blockquote>1.很多教程,其实是为那些有计算机背景的“假新手”准备的,并不是面向编程0基础的人<br>2.知识量纷乱复杂,很多毫无意义,无法准确提取有效信息<br>3.一些很优秀的文章需要极高的理解门槛</blockquote>
<p>在痛苦的工作了两个月后,我深感有必要记录下我的学习过程,希望能得到大牛指点的同时,也能帮到想要学习Python,但是毫无编程经验的朋友(尤其是女生)~ </p>
<p>如果你也是金融从业者,每天需要处理成堆excel,想要提升工作效率,亦或是在校的非计算机专业学生,想要学习pytho(相信我,Python应用的领域无处不在), 那就请关注我吧~ 毕竟只有小白才能理解小白!!</p>
<p>介绍完结,撒花~</p>