编程能力 Python注意点总结

Posted by SunQH Blog on February 8, 2021

Python zip() 函数

Python 内置函数

描述

zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。

如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同,利用 * 号操作符,可以将元组解压为列表

zip 方法在 Python 2 和 Python 3 中的不同:在 Python 3.x 中为了减少内存,zip() 返回的是一个对象。如需展示列表,需手动 list() 转换。

语法

zip 语法:

1
zip([iterable, ...])

参数说明:

  • iterabl – 一个或多个迭代器;

返回值

返回元组列表。

实例

1
2
3
4
5
6
7
8
9
10
11
以下实例展示了 zip 的使用方法

>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b)     # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c)              # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped)          # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]

zip打包为元组的列表

unzip与 zip 相反,*zipped 可理解为解压,返回二维矩阵式

zip(* matrix[1:])相当于矩阵转置

Sort()

根据自定义规则排序

strs.sort(key = functools.cmp_to_key(sort_rule))

1
2
3
4
5
6
7
def sort_rule(x, y):
  a, b = x + y, y + x
  if a > b: return 1
  elif a < b: return -1
  else: return 0

strs.sort(key = functools.cmp_to_key(sort_rule)) 

数字字符串比较大小

注意位数要写对

'00'<=num[i-1:i+1]<='09'

'0'<=num[i-1:i+1]<='9'

字典

为什么Python 3.6以后字典有序并且效率更高?

为什么Python 3.6以后字典有序并且效率更高?

排序-sorted(res_dict.items())

先用items属性将字典变为可迭代对象,再进行排序。key 默认安装key值排序。

1
2
for (index,num) in sorted(res_dict.items(), key=lambda x:x[0], reverse=False ):
    print(index,num)

ASCII码

ord()、chr()

ord(‘a’) == 65

chr(65) == ‘a’

例1、大小写字母转换:

str=input(‘输入大写字母:’)

chr(ord(str)+32)) #先将字符通过ord函数转换成ASCII码,然后+32从大写变成小写(小变大-32),再通过chr函数转换成字符

例2、字符型数字和int型数字转换:

print( chr(ord(‘1’)+3))#现将字符1转换成ASCII码,再+3后装换回字符

输出结果:4

注意数字不能合起来用ascii比较

如:

1
2
>>> '0' <= '1A' <= '99'
True

seaborn.pairplot

[Python可视化 Seaborn5分钟入门(七)——pairplot](https://zhuanlan.zhihu.com/p/98729226)
  • pairplot主要展现的是变量两两之间的关系(线性或非线性,有无较为明显的相关关系),照例来总览一下pairplot的API。

  • seaborn.pairplot(data, hue=None, hue_order=None, palette=None, vars=None, x_vars=None, y_vars=None, kind=’scatter’, diag_kind=’auto’, markers=None, height=2.5, aspect=1, corner=False, dropna=True, plot_kws=None, diag_kws=None, grid_kws=None, size=None)

    Plot pairwise relationships in a dataset.

    • kind:用于控制非对角线上的图的类型,可选”scatter”与”reg”

      diag_kind:控制对角线上的图的类型,可选”hist”与”kde”

    • hue :针对某一字段进行分类

    • palette:控制色调

    • markers:控制散点的样式

n折交叉验证

n 折交叉验证, 按照比例选择索引

1
2
3
4
5
6
7
8
9
10
11
12
13
# n 折交叉验证
>>> import numpy as np
>>> from sklearn.model_selection import KFold
# >>> X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
>>> X = np.array([[1, 1], [2, 2], [3, 3], [4, 4], [4, 4]])
>>> y = np.array([1, 2, 3, 4,5])
>>> kf = KFold(n_splits=5)
>>> kf.get_n_splits(X)
print(kf)
>>> for train_index, test_index in kf.split(X):
...     print("TRAIN:", train_index, "TEST:", test_index)
...     X_train, X_test = X[train_index], X[test_index]
...     y_train, y_test = y[train_index], y[test_index]

out

1
2
3
4
5
6
KFold(n_splits=5, random_state=None, shuffle=False)
TRAIN: [1 2 3 4] TEST: [0]
TRAIN: [0 2 3 4] TEST: [1]
TRAIN: [0 1 3 4] TEST: [2]
TRAIN: [0 1 2 4] TEST: [3]
TRAIN: [0 1 2 3] TEST: [4]

np转一维函数 — ravel()、flatten()、squeeze()

numpy中的ravel()、flatten()、squeeze()都有将多维数组转换为一维数组的功能,区别: ravel():如果没有必要,不会产生源数据的副本 flatten():返回源数据的副本 squeeze():只能对维数为1的维度降维

numpy 数组属性查看

1
2
3
4
5
6
print("数据类型",type(a1))           #打印数组数据类型  
print("数组元素数据类型:",a1.dtype) #打印数组元素数据类型  
print("数组元素总数:",a1.size)      #打印数组尺寸,即数组元素总数  
print("数组形状:",a1.shape)         #打印数组形状  
print("数组的维度数目",a1.ndim)      #打印数组的维度数目

pandas数据处理三板斧 — map、apply、applymap

在日常的数据处理中,经常会对一个DataFrame进行逐行、逐列和逐元素的操作,对应这些操作,Pandas中的mapapplyapplymap可以解决绝大部分这样的数据处理需求。

保存模型

1
2
3
4
# 保存模型
import pickle
s = pickle.dumps(clf)
clf2 = pickle.loads(s)

保存最佳模型(早停加lr衰减) — deepshare_nlp_ch4(bert)

save_model = './bert.bin'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
	"""
	如果: 模型在验证集效果比最佳的好,则保存模型
            否则, early_stop+=1,
            如果early_stop`3:停止训练
	"""
	def train(self):
        logging.info('Start training...')
        for epoch in range(1, epochs + 1):
            train_f1 = self._train(epoch)

            dev_f1 = self._eval(epoch)
		
            if self.best_dev_f1 <= dev_f1:
                logging.info(
                    "Exceed history dev = %.2f, current dev = %.2f" % (self.best_dev_f1, dev_f1))
                torch.save(self.model.state_dict(), save_model)

                self.best_train_f1 = train_f1
                self.best_dev_f1 = dev_f1
                self.early_stop = 0
            else:
                self.early_stop += 1
                if self.early_stop ` early_stops:
                    logging.info(
                        "Eearly stop in epoch %d, best train: %.2f, dev: %.2f" % (
                            epoch - early_stops, self.best_train_f1, self.best_dev_f1))
                    self.last_epoch = epoch
                    break
     """测试函数, 加载保存的模型并使用验证函数"""
    def test(self):
        self.model.load_state_dict(torch.load(save_model))
        self._eval(self.last_epoch + 1, test=True)

日志式输出

1
2
3
4
5
import logging

logging.basicConfig(level=logging.INFO, format='%(asctime)-15s %(levelname)s: %(message)s')

logging.info("Use cuda: %s, gpu id: %d.", use_cuda, gpu)

数组索引不到右边界

1
2
3
4
a = [0,1,2,3,4,5,6]
len(a[:-1]) # 6
len(a[:len(a)]) #7

字符串

判断包含数字大小写的方法

1
2
3
4
5
6
7
8
9
        for e in data:
            if e.isdigit():
                a = 1
            elif e.islower():
                b = 1
            elif e.isupper():
                c = 1
            else:
                d = 1

判断字符串里子串数量方法

1
data.count(data[j:j+3])

collections - 容器数据类型

namedtuple() 创建命名元组子类的工厂函数
deque 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop)
ChainMap 类似字典(dict)的容器类,将多个映射集合到一个视图里面
Counter 字典的子类,提供了可哈希对象的计数功能
OrderedDict 字典的子类,保存了他们被添加的顺序
defaultdict 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值
UserDict 封装了字典对象,简化了字典子类化
UserList 封装了列表对象,简化了列表子类化
UserString 封装了列表对象,简化了字符串子类化

Counter()

1
2
3
4
c = Counter()                           # a new, empty counter
c = Counter('gallahad')                 # a new counter from an iterable
c = Counter({'red': 4, 'blue': 2})      # a new counter from a mapping
c = Counter(cats=4, dogs=8)             # a new counter from keyword args
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> for a in c.items():
...     print(a)
...
('red', 2)
('blue', 3)
('green', 1)

>>> for a in c.elements():
...     print(a)
...
red
red
blue
blue
blue
green
  • elements()

    返回一个迭代器,其中每个元素将重复出现计数值所指定次。 元素会按首次出现的顺序返回。 如果一个元素的计数值小于一,elements() 将会忽略它。»>>>> c = Counter(a=4, b=2, c=0, d=-2) >>> sorted(c.elements()) ['a', 'a', 'a', 'a', 'b', 'b']

  • most_common([n])

    返回一个列表,其中包含 n 个最常见的元素及出现次数,按常见程度由高到低排序。 如果 n 被省略或为 Nonemost_common() 将返回计数器中的 所有 元素。 计数值相等的元素按首次出现的顺序排序:»>>>> Counter('abracadabra').most_common(3) [('a', 5), ('b', 2), ('r', 2)]

  • subtract([iterable-or-mapping])

    迭代对象映射对象 减去元素。像 dict.update() 但是是减去,而不是替换。输入和输出都可以是0或者负数。»>>>> c = Counter(a=4, b=2, c=0, d=-2) >>> d = Counter(a=1, b=2, c=3, d=4) >>> c.subtract(d) >>> c Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6}) 3.2 新版功能.

通常字典方法都可用于 Counter 对象,除了有两个方法工作方式与字典并不相同。

  • fromkeys(iterable)

    这个类方法没有在 Counter 中实现。

  • update([iterable-or-mapping])

    迭代对象 计数元素或者 从另一个 映射对象 (或计数器) 添加。 像 dict.update() 但是是加上,而不是替换。另外,迭代对象 应该是序列元素,而不是一个 (key, value) 对。

deque()

  • append(x)

    添加 x 到右端。

  • appendleft(x)

    添加 x 到左端。

  • clear()

    移除所有元素,使其长度为0.

  • copy()

    创建一份浅拷贝。3.5 新版功能.

  • count(x)

    计算 deque 中元素等于 x 的个数。3.2 新版功能.

  • extend(iterable)

    扩展deque的右侧,通过添加iterable参数中的元素。

  • extendleft(iterable)

    扩展deque的左侧,通过添加iterable参数中的元素。注意,左添加时,在结果中iterable参数中的顺序将被反过来添加。

  • index(x[, start[, stop]])

    返回 x 在 deque 中的位置(在索引 start 之后,索引 stop 之前)。 返回第一个匹配项,如果未找到则引发 ValueError3.5 新版功能.

  • insert(i, x)

    在位置 i 插入 x 。如果插入会导致一个限长 deque 超出长度 maxlen 的话,就引发一个 IndexError3.5 新版功能.

  • pop()

    移去并且返回一个元素,deque 最右侧的那一个。 如果没有元素的话,就引发一个 IndexError

  • popleft()

    移去并且返回一个元素,deque 最左侧的那一个。 如果没有元素的话,就引发 IndexError

  • remove(value)

    移除找到的第一个 value。 如果没有的话就引发 ValueError

  • reverse()

    将deque逆序排列。返回 None3.2 新版功能.

  • rotate(n=1)

    向右循环移动 n 步。 如果 n 是负数,就向左循环。如果deque不是空的,向右循环移动一步就等价于 d.appendleft(d.pop()) , 向左循环一步就等价于 d.append(d.popleft())

Deque对象同样提供了一个只读属性:

  • maxlen

    Deque的最大尺寸,如果没有限定的话就是 None

python线程

问题1-终止tkinter中的python线程

终止线程的方法我采取的方案是利用全局实时变化的变量标志位进行判断终止。

问题2-跨文件无法进行传参

在比较复杂的工程中会存在Python跨文件全局变量的使用

python跟C不一样,c是在一个文件定义后在另一个文件声明下是extern变量就好。python则是通过global声明,但作用域依旧是单个文件。

有一种方式是在A定义,在B import。这种方式,如果仅存在B import A ,那没问题。但是如果A又存在import B则会报错,原因是出现循环调用。解决办法也有几种,这里不展开。这种方式,有一个问题,就是无法实时传递变量,B import后,A中发生了变化,B是不知道的。

于是乎,不如单独拿一个py文件来单独存放这些全局变量,其他文件都可以单向import

单独搞一个global_var.py如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: utf-8 -*-

def _init():  # 初始化
    global _global_dict
    _global_dict = {}

def set_value(key, value):
    #定义一个全局变量
    _global_dict[key] = value

def get_value(key):
    #获得一个全局变量,不存在则提示读取对应变量失败
    try:
        return _global_dict[key]
    except:
        print('读取'+key+'失败\r\n')

其他文件需要用到的,则import global_var.py。然后在主文件初始化一下,global_var._init()

接着便可以随便使用了,比如先定义(定义字典的时候顺便写入初始值):

1
2
3
4
5
6
7
8
9
def task2_train():    
  global_var.set_value('flag_run_task', 1)    
  train_2.train()
def task2_test():    
  global_var.set_value('flag_run_task', 2)
  test_2.test()
def task3_train(): 
  global_var.set_value('flag_run_task', 3)    
  main_3.run()

使用的时候,直接get下字典对应键值,在循环中做如下实现:

1
2
3
4
while True:    
  if global_var.get_value('flag_run_task') != 1:       
    sys.exit()
  ## do something

另外,如果是多线程,可能会出现一个线程写入这个变量,另一个线程又在读取,从可靠性的角度考虑,多线程时读写最好加个锁同步一下

主循环

定义了一个函数一直不出图,最后发现是循环的问题,最后要加上:

1
root.mainloop()

tkinter 控件

img

滚动文本框ScrolledText

grid是布局方法,也就是说我们每次设置一个控件都需要将其grid,否则将不显示

滚动文本框ScrolledText,其可以设置字体,颜色等参数

1
2
3
text=ScrolledText(root,font=('微软雅黑',10),fg='blue')

text.grid()

label

可变的文本,文本我们采取的也是放在label里的方式

1
2
3
4
var=StringVar()#设置变量
label=Label(root,font=('微软雅黑',10),fg='red',textvariable=var)
label.grid()
var.set('sth')

如果在程序执行中,需要对文本进行更改时,可以再次使用var.set

tkinter+多线程

TKinter自身刷新GUI是单线程的,用户在调用mainloop方法后,主线程会一直不停循环刷新GUI,但是如果用户给某个widget绑定了一个很耗时的方法A时,这个方法A也是在主线程里调用,于是这个耗时的方法A会阻塞住刷新GUI的主线程,表现就是整个GUI卡住了,只有等这个耗时的方法结束后,GUI才会对用户操作响应

最简单的解决上述问题的办法就是利用多线程,把两个耗时方法丢到两个子线程里运行,就可以避开主线程被阻塞的问题

threading多线程模块

1
threading.Thread(target=函数名args=(调用参数注意最后要有一个,))`

threading可以实例化一个多线程对象,使用th.setDaemon(True)守护线程,通过start就可以开启。

说一下th.setDaemon(True)守护线程:

python中得thread的一些机制和C/C++不同:在C/C++中,主线程结束后,其子线程会默认被主线程kill掉。而在python中,主线程结束后,会默认等待子线程结束后,主线程才退出。

那么尴尬的问题就出现了,有一种情况,比如说在linux系统中存在脱离于终端的线程,即父进程在创建子进程之后先于子进程退出,会造成这个子进程变为“孤儿进程”

如果你忘记杀死子线程,那么好了,你杀主线程的时候其就会挂起等待直至子线程结束,所以为了避免这样的情况,python有两个函数管理线程joinsetDaemon

join:如在一个线程B中调用threada.join(),则threada结束后,线程B才会接着threada.join()往后运行。 setDaemon:主线程A启动了子线程B,调用b.setDaemaon(True),则主线程结束时,会把子线程B也杀死,与C/C++中得默认效果是一样的。

一般情况下,在start前面加一下setDaemon(True)

1
2
3
4
5
def fun():
    for i in range(1, 5+1):
        th=threading.Thread(target=count,args=(i,))
        th.setDaemon(True)#守护线程
        th.start()

原始按钮函数

1
2
self.button_1 = Button(root, text="run A", width=10, command=self.A)
self.button_1.pack(side="top")
1
2
3
4
def A(self):
    print("start to run proc A")
    time.sleep(3)
    print("proc A finished")

加入多线程防止GUI主线程被阻塞

利用多线程,可以把两个耗时方法丢到两个子线程里运行,就可以避开主线程被阻塞的问题

1
2
3
4
5
6
7
8
9
    def __A(self):
        print("start to run proc A")
        time.sleep(3)
        print("proc A finished")

    # 子线程构建
    def A(self):
        T = threading.Thread(target=self.__A)
        T.start()

对输出进行重定向

当有需要在text上显示的输出时,需要将标准输出stdout重定向到了一个自定义的类里,这个类的write方法可以更改Text的内容

1
sys.stdout = re_Text(self.text)

在界面中显示子线程输出

经过重定向后,我们需要定义一个如下函数展示输出:

1
2
3
4
5
6
	def show(self):
        i = 0
        while i < 3:
            print fmtTime(time.time())
            time.sleep(1)
            i += 1

需要注意的是,我们尽量不应该把上面代码里的show方法丢到子线程里去跑。主要原因如下:

(1)上述代码里,利用子线程去更新Text是不安全的,因为可能存在不同线程同时修改Text的情况,这样可能导致打印出来的内容直接混乱掉。

(2)上述代码可能会直接报错,这个应该和TCL版本有关,在Google,Stackoverflow上基本都是不推荐子线程里更新GUI。

目前,比较好的解决GUI阻塞,而且不在子线程里更新GUI的办法,还是利用python自带的队列Queue,以及Tkinter下面的after方法。首先说下Queue这个class,Queue是python自带的一个队列class,其应用遵循着先入先出的原则。利用Queue.put方法将元素推进Queue里,再利用Queue.get方法将元素取出,最先推进去的元素会被最先取出。看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
import Queue
import time
 
queue = Queue.Queue()
for i in range(0, 4):
    time.sleep(0.5)
    element = "element %s"%i
    print "put %s"%element
    queue.put(element)
 
while not queue.empty():
    time.sleep(0.5)
    print "get %s"%queue.get()

示例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# coding=utf-8
from Tkinter import *
from ttk import *
import threading
import time
import sys
import Queue
 
def fmtTime(timeStamp):
    timeArray = time.localtime(timeStamp)
    dateTime = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)
    return dateTime
 
#自定义re_Text,用于将stdout映射到Queue
class re_Text():
 
    def __init__(self, queue):
 
        self.queue = queue
 
    def write(self, content):
 
        self.queue.put(content)
 
class GUI():
 
    def __init__(self, root):
 
        #new 一个Quue用于保存输出内容
        self.msg_queue = Queue.Queue()
        self.initGUI(root)
 
    #在show_msg方法里,从Queue取出元素,输出到Text
    def show_msg(self):
 
        while not self.msg_queue.empty():
            content = self.msg_queue.get()
            self.text.insert(INSERT, content)
            self.text.see(END)
 
        #after方法再次调用show_msg
        self.root.after(100, self.show_msg)
 
    def initGUI(self, root):
 
        self.root = root
        self.root.title("test")
        self.root.geometry("400x200+700+500")
        self.root.resizable = False
 
        self.button = Button(self.root, text="click", width=10, command=self.show)
        self.button.pack(side="top")
 
        self.scrollBar = Scrollbar(self.root)
        self.scrollBar.pack(side="right", fill="y")
 
        self.text = Text(self.root, height=10, width=45, yscrollcommand=self.scrollBar.set)
        self.text.pack(side="top", fill=BOTH, padx=10, pady=10)
        self.scrollBar.config(command=self.text.yview)
 
        #启动after方法
        self.root.after(100, self.show_msg)
 
        #将stdout映射到re_Text
        sys.stdout = re_Text(self.msg_queue)
 
        root.mainloop()
 
    def __show(self):
 
        i = 0
        while i < 3:
            print fmtTime(time.time())
            time.sleep(1)
            i += 1
 
    def show(self):
        T = threading.Thread(target=self.__show, args=())
        T.start()
 
if __name__ == "__main__":
 
    root = Tk()
    myGUI = GUI(root)

示例2:

打印线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# coding:utf-8
from tkinter import *
# from tkinter.ScrolledText import ScrolledText  # 文本滚动条
import threading
import time
from PIL import ImageTk, Image


def count(i):
    for k in range(1, 100 + 1):
        text.insert(END, '第' + str(i) + '线程count:  ' + str(k) + '\n')
        time.sleep(0.001)


def fun():
    for i in range(1, 5 + 1):
        th = threading.Thread(target=count, args=(i,))
        th.setDaemon(True)  # 守护线程
        th.start()
    var.set('MDZZ')


root = Tk()
throot.geometry('+600+100')  # 窗口呈现位置
text = Text(root, font=('微软雅黑', 10), fg='blue')
text.grid()
button = Button(root, text='sth', font=('微软雅黑', 10), command=fun)
button.grid()
var = StringVar()  # 设置变量
label = Label(root, font=('微软雅黑', 10), fg='red', textvariable=var)
label.grid()
var.set('sth')
root.mainloop()

示例3:

按钮调用方法函数:

1
tk.Button(root,text="开始下载",width=3*L,command=lambda :thread_fun(down_by_stock)).grid(row=2+start_row,column=0,columnspan=2,padx=2*L,pady=L)

开启线程函数:

1
2
3
4
def thread_fun(fun):
    thread=threading.Thread(target=fun)
    thread.setDaemon(True)
    thread.start()

【Python】TKinter在多线程时刷新GUI的一些碎碎念

https://blog.csdn.net/sm9sun/article/details/53743116

关于深拷贝浅拷贝,共享内存

注意python单变量是不共享内存的,可以连等复制,数组类型就不可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
In [1]: a = b = c = []

In [2]: a.append(1)

In [3]: a
Out[3]: [1]

In [4]: b
Out[4]: [1]

In [5]: a = b = c =1

In [6]: a
Out[6]: 1

In [7]: a = 2

In [8]: b
Out[8]: 1

In [9]: a
Out[9]: 2

所以最保险的写法就是分开写

1
len_tf, len_rake, len_tf_find, len_rake_find = [], [], [], []

filter函数

Python 语言提供 filter() 函数,语法如下:

1
filter(function, sequence) 

filter() 函数的功能:对 sequence 中的 item 依次执行 function(item),将结果为 True 的 item 组成一个 List/String/Tuple(取决于 sequence 的类型)并返回。有了这个函数,上面的代码可以简化为:

1
2
divide_by_three = lambda x : True if x % 3 
0 else False selected_numbers = filter(divide_by_three, range(1, 11)) 

将 lambda 表达式放在语句中,代码简化到只需要一句话就够了:

1
selected_numbers = filter(lambda x: x % 3 == 0, range(1, 11))

DataFrame.apply函数

apply函数pandas里面所有函数中自由度最高的函数。该函数如下:

1
DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None, args=(), **kwds)

该函数最有用的是第一个参数,这个参数是函数,相当于C/C++的函数指针。

这个函数需要自己实现,函数的传入参数根据axis来定,比如axis = 1,就会把一行数据作为Series的数据结构传入给自己实现的函数中,我们在函数中实现对Series不同属性之间的计算,返回一个结果,则apply函数会自动遍历每一行DataFrame的数据,最后将所有结果组合成一个Series数据结构并返回。

DataFrame.apply() 函数则会遍历每一个元素,对元素运行指定的 function。比如下面的示例:

1
2
3
4
5
6
7
8
9
10
11
import pandas as pd
import numpy as np

matrix = [
    [1,2,3],
    [4,5,6],
    [7,8,9]
]

df = pd.DataFrame(matrix, columns=list('xyz'), index=list('abc'))
df.apply(np.square)

如果只想 apply() 作用于指定的行和列,可以用行或者列的 name 属性进行限定。比如下面的示例将 x 列进行平方运算:

1
df.apply(lambda x : np.square(x) if x.name=='x' else x)
1
2
3
4
    x  y  z
a   1  2  3
b  16  5  6
c  49  8  9

下面的示例对 x 和 y 列进行平方运算:

1
df.apply(lambda x : np.square(x) if x.name in ['x', 'y'] else x)
1
2
3
4
    x   y  z
a   1   4  3
b  16  25  6
c  49  64  9

下面的示例对第一行 (a 标签所在行)进行平方运算:

1
df.apply(lambda x : np.square(x) if x.name == 'a' else x, axis=1)

默认情况下 axis=0 表示按列,axis=1 表示按行。