美文网首页
《利用Python进行数据分析·第2版》【Chapter 3.1

《利用Python进行数据分析·第2版》【Chapter 3.1

作者: 蜘蛛的梦呓 | 来源:发表于2018-05-19 19:58 被阅读0次

    【Chapter 3】 Python的数据结构、函数和文件

    本章讨论 Python 的内置功能,我们会从Python最基础的数据结构开始:元组、列表、字典和集合。然后会讨论创建你自己的、可重复使用的Python函数。最后,会学习Python的文件对象,以及如何与本地硬盘交互。

    3.1 数据结构和序列

    元组(Tuple)

    元组是长度固定,不可改变的序列(可以看做长度固定,且不可变的列表)。创建元祖的方法是用逗号:

    1. 合并元组

    用 “+” 合并元组。

    In [11]: (4,5,6)+(7,8)
    Out[11]: (4, 5, 6, 7, 8)
    

    2. 取出元组

    In [14]: tup = 4, 5, (6, 7)
        ...: a, b, (c, d) = tup
        ...: 
    
    In [15]: d
    Out[15]: 7
    

    交换变量名

    In [19]: a
    Out[19]: 4
    
    In [20]: b
    Out[20]: 5
    
    In [21]: b,a = a,b
    
    In [22]: a
    Out[22]: 5
    
    In [23]: b
    Out[23]: 4
    

    类似上例的 unpacking 方法常用来迭代元组或列表序列:

    In [35]: for a,b,c in seq:
        ...:     print('a={0},b={1},c={2}'.format(a,b,c))
        ...:     
    a=1,b=2,c=3
    a=4,b=5,c=6
    a=7,b=8,c=9
    

    另一种更高级的 unpacking 方法是用于只取出tuple中开头几个元素,剩下的元素直接赋给*rest

    In [36]: values = 1,2,3,4,5
    
    In [37]: a,b,*rest = values
    
    In [38]: a,b
    Out[38]: (1, 2)
    
    In [39]: rest
    Out[39]: [3, 4, 5]
    

    rest 部分是你想要丢弃的,名字本身无所谓,通常用下划线来代替:

    In [40]: a, b, *_ = values
    
    In [41]: _
    Out[41]: [3, 4, 5]
    
    3. Tuple methods(元组方法)

    因为tuple的大小和内容都不能改变,所以方法也很少。count用来计算某个值出现的次数,list中也有这个方法:

    In [42]: a = (1, 2, 2, 2, 3, 4, 2)
        ...: a.count(2)
        ...: 
    Out[42]: 4
    

    List(列表)

    列表的灵活性就很强了,大小和内容都可以变。

    list 函数通常用来具现化迭代器或生成器:

    In [43]: gen =range(10)
    
    In [44]: lis(gen)
    
    In [45]: list(gen)
    Out[45]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    
    1. 添加

    list.append() 在列表末尾添加;

    list.insert(index,element) 可以在特定的位置插入元素 ;

    警告:与append相比,insert耗费的计算量大,因为对后续元素的引用必须在内部迁移,以便为新元素提供空间。如果要在序列的头部和尾部插入元素,你可能需要使用collections.deque,一个双尾部队列。

    2.删除

    insert的逆运算是pop(index),它移除并返回指定位置的元素;

    可以用remove去除某个值,remove('element')会先寻找第一个值并除去;

    如果不考虑性能,使用appendremove,可以把Python的列表当做完美的“多重集”数据结构。

    3.包含

    in可以检查列表是否包含某个值:

    In [55]: 'dwarf' in b_list
    Out[55]: True
    
    4. 串联和组合列表

    与元组类似,可以用加号将两个列表串联起来:

    In [57]: [4, None, 'foo'] + [7, 8, (2, 3)]
    Out[57]: [4, None, 'foo', 7, 8, (2, 3)]
    

    如果已经定义了一个列表,用extend方法可以追加多个元素:

    In [58]: x = [4, None, 'foo']
    
    In [59]: x.extend([7, 8, (2, 3)])
    
    In [60]: x
    Out[60]: [4, None, 'foo', 7, 8, (2, 3)]
    

    通过加法将列表串联的计算量较大,因为要新建一个列表,并且要复制对象。

    extend追加元素,尤其是到一个大列表中,更为可取。因此:

    everything = []
    for chunk in list_of_lists:
        everything.extend(chunk)
    

    要比串联方法快:

    everything = []
    for chunk in list_of_lists:
        everything = everything + chunk
    

    总结:

    append 和 extend 的区别。

    • append 是把元素添加到一个list里
    • extend 是把两个 list (多个元素)结合在一起

    extend 和 + 的区别

    • + 是创建了一个新的list并返回,运算量大
    • extend 是在原本的list上做了更改,运算量小
    5. 排序

    你可以用sort函数将一个列表原地排序(不创建新的对象):

    In [61]: a = [7, 2, 5, 1, 3]
    
    In [62]: a.sort()
    
    In [63]: a
    Out[63]: [1, 2, 3, 5, 7]
    

    sort有一些选项,有时会很好用。其中之一是二级排序key,可以用这个key进行排序。例如,我们可以按长度对字符串进行排序:

    In [64]: b = ['saw', 'small', 'He', 'foxes', 'six']
    
    In [65]: b.sort(key=len)
    
    In [66]: b
    Out[66]: ['He', 'saw', 'six', 'small', 'foxes']
    

    二分搜索和维持一个排好序的 list 列表

    bisect模块支持二分查找,和向已排序的列表插入值。:

    bisect.bisect 其目的在于查找该数值将会插入的位置并返回,而不会插入。

    bisect.insort 是插入一个值,而插入后的列表还是整齐的。

    In [47]: c= [1,2,2,2,3,4,7]
    
    In [48]: bisect.bisect(c,2)
    Out[48]: 4
    
    In [49]: bisect.bisect(c,5)
    Out[49]: 6
    
    In [50]: bisect.bisect(c,6)
    Out[50]: 6
    
    In [51]: c
    Out[51]: [1, 2, 2, 2, 3, 4, 7]
    

    注意:bisect模块不会检查list是否是排好序的,对未排序的列表使用bisect不会产生错误,但结果不一定正确 ,所以用这个模块之前要先把list排序。

    Slicing (切片)

    [start:stop], 输出的结果包含开头,不包含结尾。所以输出的结果的数量是stop-start 。即区间为[start,stop)。

    In [73]: seq = [7, 2, 3, 7, 5, 6, 0, 1]
    
    In [74]: seq[1:5]
    Out[74]: [2, 3, 7, 5]
    

    startstop 都可以被省略,省略之后,分别默认序列的开头和结尾(负索引表示倒数开始多少个的意思):

    In [77]: seq[:5]
    Out[77]: [7, 2, 3, 6, 3]
    
    In [78]: seq[-4:]
    Out[78]: [5, 6, 0, 1]
    

    两个冒号后面的数代表步长(step),就是隔(step-1)个元素取一次:

    In [80]:seq
    Out[80]:[7, 2, 3, 6, 3, 5, 6, 0, 1]
    
    In [81]: seq[::2]
    Out[81]: [7, 3, 3, 6, 1]
    

    序列函数

    1. enumerate(枚举)函数

    迭代一个序列时,你可能想跟踪当前项的序号 。一个比较直白的方法是:

    i = 0
    for value in collection:
        # do something with value
        i += 1
    

    但 Python 内建了一个enumerate函数,可以返回(i, value)元组序列:

    for i, value in enumerate(collection):
    

    enumerate 通常用来把一个 list 中的位置和值映射到一个 dcit 字典里:

    In [57]: some_list = ['foo','bar','baz']
    
    In [58]: mapping={}
    
    In [59]: for i,v in enumerate(some_list):
        ...:     mapping[v] = i
        ...:     
    
    In [60]: mapping
    Out[60]: {'bar': 1, 'baz': 2, 'foo': 0}
    
    2. sorted()

    sorted函数可以从任意序列的元素返回一个新的排好序的列表:

    In [71]: sorted([1,23,4,5,2,7])
    Out[71]: [1, 2, 4, 5, 7, 23]
    
    In [72]: sorted('jadon sunshine')
    Out[72]: [' ', 'a', 'd', 'e', 'h', 'i', 'j', 'n', 'n', 'n', 'o', 's', 's', 'u']
    

    sorted函数可以接受和sort相同的参数。

    3. zip 函数

    用于"pairs"(成对)。把多个列表、元组或其它序列每个对应的元素变成一对,最后返回一个含有 tuple 的 list:

    In [73]: seq1 = ['foo', 'bar', 'baz']
    
    In [74]: seq2 = ['one', 'two', 'three']
    
    In [75]: zipped = zip(seq1,seq2)
    
    In [76]: list(zipped)
    Out[76]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
    

    zip 可以接收任意长度的序列,最后返回的结果取决于最短的序列:

    In [77]: seq3 = [False, True]
        ...: list(zip(seq1, seq2, seq3))
        ...: 
    Out[77]: [('foo', 'one', False), ('bar', 'two', True)]
    

    zip 的一个常见用法是同时迭代多个序列,可以和 enumerate 搭配起来用:

    In [79]: for i,(a,b) in enumerate(zip(seq1,seq2)):
        ...:     print('{0}:{1},{2}'.format(i,a,b))
        ...:     
    0:foo,one
    1:bar,two
    2:baz,three
    

    如果给我们一个压缩过的序列,我们可以将其解压:

    
    In [81]: pitchers = [('Nolan', 'Ryan'), ('Roger', 'Clemens'), 
        ...:             ('Schilling', 'Curt')]
        ...:             
    
    In [82]: first_names,last_names = zip(*pitchers) #解压
    In [84]: first_names
    Out[84]: ('Nolan', 'Roger', 'Schilling')
    In [85]: last_names
    Out[85]: ('Ryan', 'Clemens', 'Curt')
    
    5.reversed

    reverse可以倒叙迭代序列:

    In [86]: list(reversed(range(10)))
    Out[86]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    

    注意,revered是一个generator(生成器,之后会详细讲),所以必须需要(list 或 for循环)来具现化 。

    字典

    1. 字典创建、访问、插入、更改

    字典也被叫做hash map 或 associative array。结构就是key-value pairs.创建方式是用{key : value}:

    In [101]: empty_dict = {}
    
    In [102]: d1 = {'a' : 'some value', 'b' : [1, 2, 3, 4]}
    
    In [103]: d1
    Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}
    

    你可以像访问列表或元组中的元素一样,访问、插入或设定字典中的元素:

    In [87]: d1 = {'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [88]: d1[7] ='an integer'#插入
    
    In [90]: d1
    Out[90]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [91]: d1['b']#访问
    Out[91]: [1, 2, 3, 4]
    

    你可以用检查列表和元组是否包含某个值得方法,检查字典中是否包含某个键:

    In [93]: 'b' in d1
    Out[93]: True
    

    可以用del关键字或pop方法(返回值得同时删除键)删除值:

    In [94]: d1[5] = 'some value'
    
    In [95]: d1
    Out[95]: {5: 'some value', 7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [96]: del d1[5]
    
    In [97]: d1
    Out[97]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [101]: d1
    Out[101]: {7: 'an integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [102]: ret = d1.pop(7)
    
    In [103]: d1
    Out[103]: {'a': 'some value', 'b': [1, 2, 3, 4]}
    

    keys 和 values 方法能返回dict中key-value组合的迭代器,不过并不按什么顺序。如果想让 keys 和 values 每次打印的顺序相同的话:

    In [106]: d1
    Out[106]: {7: 'integer', 'a': 'some value', 'b': [1, 2, 3, 4]}
    
    In [108]: list(d1.keys())
    Out[108]: ['a', 'b', 7]
    
    In [110]: list(d1.values())
    Out[110]: ['some value', [1, 2, 3, 4], 'integer']
    

    update方法可以将一个字典与另一个融合:

    In [119]: d1.update({'b' : 'foo', 'c' : 12})
    
    In [120]: d1
    Out[120]: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
    

    update方法是原地改变字典,因此任何传递给update的键的旧的值都会被舍弃。

    2.用序列创建字典

    常常,你可能想将两个序列配对组合成字典。下面是一种写法:

    mapping = {}
    for key, value in zip(key_list, value_list):
        mapping[key] = value
    

    因为字典本质上是 2元元组 的集合,dict可以接受 2元元组的列表:

    In [121]: mapping = dict(zip(range(5), reversed(range(5))))
    
    In [122]: mapping
    Out[122]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}
    
    3. Default value(默认值)

    如果 dict 中某个key存在的话,就返回该value,否则的话,就返回一个默认值:

    if key in some_dict:
        value = some_dict[key]
    else:
        value = default_value
    

    不过dict中的get 和pop方法能设置默认值,即能把上面的代码简写为:

    value = some_dict.get(key, default_value)
    

    如果key不存在的话,get方法默认会返回None,而pop则会引发一个错误。

    通过设定值,一个常用的场景是一个dict中的value也是其他集合,比如list。举例说明,我们想要把一些单词按首字母归类:

    In [128]: words = ['apple', 'bat', 'bar', 'atom', 'book']
         ...: by_letter = {}
         ...: 
    
    In [129]: for word in words:
         ...:     letter = word[0]
         ...:     if letter not in by_letter:
         ...:         by_letter[letter] = [word]#新建一个 key:value
         ...:     else :
         ...:         by_letter[letter].append(word)#如果已经存在这个 key 直接添加 value
         ...:         
    
    In [129]: 
    
    In [130]: by_letter
    Out[130]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
    

    而 setdefault 方法则是专门为这个用途存在的,上面的循环可以写为:

    for word in words:
        letter = word[0]
        by_letter.setdefault(letter, []).append(word)
    

    使用setdefault() 初始化字典键值. 使用字典的时候经常会遇到这样一种应用场景:动态更新字典,像如上面代码,如果 key 不在 dictionary 中那么就添加它并把它对应的值初始为空列表[] ,然后把元素append到空列表中。

    内建的collections模块有一个有用的class,defaultdict,这个能让上述过程更简单。创建方法是传递一个type或是函数:

    from collections import defaultdict
    by_letter = defaultdict(list)
    for word in words:
        by_letter[word[0]].append(word)
    

    Valid dict key types(有效的key类型)

    通常key的类型是不可更改的常量类型(int,float,string)或tuple。专业的叫法是hashability。可以查看一个object是否是hashable,只要是hashable的,就可以当做dict中的key。这里用hash函数查看:

    In [127]: hash('string')
    Out[127]: 5023931463650008331
    
    In [128]: hash((1, 2, (2, 3)))
    Out[128]: 1097636502276347782
    
    In [129]: hash((1, 2, [2, 3])) # fails because lists are mutable
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-129-800cd14ba8be> in <module>()
    ----> 1 hash((1, 2, [2, 3])) # fails because lists are mutable
    TypeError: unhashable type: 'list'
    
    

    要用列表当做键,一种方法是将列表转化为元组,只要内部元素可以被哈希,它也就可以被哈希:

    In [131]: d = {}
    
    In [132]: d[tuple([1,2,3])]=5
    
    In [133]: d
    Out[133]: {(1, 2, 3): 5}
    

    Set 集合

    集合是无序的不可重复的元素的集合。你可以把它当做字典,但是只有键没有值。可以用两种方式创建集合:通过 se t函数或使用尖括号 set 语句:

    In [133]: set([2, 2, 2, 1, 3, 3])
    Out[133]: {1, 2, 3}
    
    In [134]: {2, 2, 2, 1, 3, 3}
    Out[134]: {1, 2, 3}
    

    集合支持合并、交集、差分和对称差等数学集合运算。考虑两个示例集合:

    In [135]: a = {1, 2, 3, 4, 5}
    
    In [136]: b = {3, 4, 5, 6, 7, 8}
    

    合并是取两个集合中不重复的元素。可以用union方法,或者|运算符:

    In [136]: # 并集
        ...: a.union(b)
    Out[136]: {1, 2, 3, 4, 5, 6, 7, 8}
    In [137]: a|b
    Out[137]: {1, 2, 3, 4, 5, 6, 7, 8}
    

    交集的元素包含在两个集合中。可以用intersection&运算符:

    In [138]: # 交集
         ...: a.intersection(b)
    Out[138]: {3, 4, 5}
    In [139]: a & b
    Out[139]: {3, 4, 5}
    

    所有逻辑集合操作都有另外原地实现方法,它可以直接用结果替代集合的内容。对于大的集合,这么做效率更高:

    In [141]: c = a.copy()
    
    In [142]: c |= b
    
    In [143]: c
    Out[143]: {1, 2, 3, 4, 5, 6, 7, 8}
    
    In [144]: d = a.copy()
    
    In [145]: d &= b
    
    In [146]: d
    Out[146]: {3, 4, 5}
    

    set 的元素必须是不可更改的。如果想要 list 一样的元素,只能变为tuple:

    In [157]: my_data = [1,2,3,4]
    
    In [158]: my_set = {tuple(my_data)}
    
    In [159]: my_set
    Out[159]: {(1, 2, 3, 4)}
    

    你还可以检测一个集合是否是另一个集合的子集或父集:

    In [150]: a_set = {1, 2, 3, 4, 5}
    
    In [151]: {1, 2, 3}.issubset(a_set)
    Out[151]: True
    
    In [152]: a_set.issuperset({1, 2, 3})
    Out[152]: True
    

    集合的内容相同时,集合才对等:

    In [153]: {1, 2, 3} == {3, 2, 1}
    Out[153]: True
    

    列表、集合和字典推导式

    list comprehension(列表推导式)是python里最受喜爱的一个特色。我们能简洁地构造一个list:

    [expr for val in collection if condiction]
    相当于:
    result = []
    for val in collection:
        if condition:
            result.append(expr)
    

    比如,给定一个list,里面有很多string,我们只要留下string长度超过2的,并将其转换为大写:

    In [160]: strings = ['a', 'as', 'bat', 'car', 'dove', 'python']
    
    In [161]: [x.upper() for x in strings if len(x) > 2]
    Out[161]: ['BAT', 'CAR', 'DOVE', 'PYTHON']
    

    dict推导式:

    dict_comp = {key-expr: value-expr for value in collection if condition}

    set的推导式:(集合的推导式与列表很像,只不过用的是尖括号: )

    set_comp = {expr for value in collection if condition}

    基于上面的例子,我们想要一个集合来保存string的长度:

    
    

    map函数可以进一步简化:

    In [158]: set(map(len, strings))
    Out[158]: {1, 2, 3, 4, 6}
    

    下面一个简单的字典表达式例子,查找 字符串和其在list中对应的index:

    In [159]: loc_mapping = {val : index for index, val in enumerate(strings)}
    
    In [160]: loc_mapping
    Out[160]: {'a': 0, 'as': 1, 'bat': 2, 'car': 3, 'dove': 4, 'python': 5}
    
    作者:SeanCheney
    链接:https://www.jianshu.com/p/b444cda10aa0
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    

    Nested list comprehensions(嵌套列表表达式)

    假设我们有一个list,list中又有不同的list表示英语和西班牙语的姓名:

    all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'], 
                ['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]
    

    你可能是从一些文件得到的这些名字,然后想按照语言进行分类。现在假设我们想用一个列表包含所有的名字,这些名字中包含两个或更多的e。可以用for循环来做:

    names_of_interest = []
    for names in all_data:
        enough_es = [name for name in names if name.count('e') >= 2]
        names_of_interest.extend(enough_es)
    

    可以用嵌套列表推导式的方法,将这些写在一起,如下所示:

    In [169]: result = [name for names in all_data for name in names if name.count('e')>=2]
    
    In [170]: result
    Out[170]: ['Steven']
    

    for部分是根据嵌套的顺序来写的,从外层的loop到内层的loop。这里一个例子是把tuple扁平化成一个整数列表:

    In [171]: some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
         ...: flattened = [x for tup in some_tuples for x in tup]
         ...: flattened
         ...: 
    Out[171]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    一定要记住顺序是和我们写for loop一样的:

    In [172]: flatteded = []
         ...: 
         ...: for tup in some_tuples:
         ...:     for x in tup:
         ...:         flattened.append(x)
    

    列表表达式里再有一个列表表达式也是可以的,可以生成a list of lists:

    In [173]: [[x for x in tup] for tup in some_tuples]
    Out[173]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    

    相关文章

      网友评论

          本文标题:《利用Python进行数据分析·第2版》【Chapter 3.1

          本文链接:https://www.haomeiwen.com/subject/bfendftx.html