python-pandas库

练习网站
https://pandas.liuzaoqi.com/doc/chapter1/从列表创建.html
参考网站
https://www.biaodianfu.com/pandas-sql.html#选择列

打开xlsx错误解决

使用pandas读取excel文件时,会调用xlrd库,由于xlrd更新至2.0.1版本后只支持xls文件,所以使用pd.read_excel(‘c:/文夹夹/aa.xlsx’)会出现报错。
解决方法:
使用pd.read_excel(‘c:/文件夹/aa.xlsx’,engine=‘openpyxl’)

Series和DataFrame之间概念区别

Series 一维数据,一行或者一列 以及转换成列表

1.1 创建一个Series,可以用列表或者一个字典,如果是列表,index值默认就是自增的id,当然也可以指定index

p = pd.Series([1,2,3,"four","5",6.0],index=["第1个","第2个","第3个","第4个","第5个","第6个"])
# print(p.index)  # Index(['第1个', '第2个', '第3个', '第4个', '第5个', '第6个'], dtype='object') 是个列表可以按列表取值
# print(p.values) #[1 2 3 'four' '5' 6.0] 此时是一个numpy的数组
# print(p.values.tolist() #[1 2 3 'four' '5' 6.0] 此时是一个数组

1.2 也可以用一个字段创建,key就是index,value就是value,字典的形式serise被包含在一个元祖内.
p1 = pd.Series({"第一个":1,"第二个":2,"第三个":3,"第四个":"four","第五个":5.0}),

2.1 数据查询

print(p[["第1个","第2个"]]) #查询多个值,返回的是series
print(p["第1个"]) #查询一个值返回的就是那个值本身

DataFrame 二维数据或者多维的数据,多行多列,除了index行索引外,还有个columns列索引

1.1 常用的DataFrame数据创建 在字典类中,字典最外层的key都作为了列索引,而值则作为某一行的值;在列表类中,列表的每一个元素都作为了某一行的值。

import pandas as pd
pth=r"C:\aaa.xlsx"
writer = pd.ExcelWriter(path=pth,engine="openpyxl",)

dic1 = {"隐患类型1": { "浙江": 2},
       "隐患类型2": {"北京": 10, "浙江": 20,"上海":30},
       "隐患类型3": {"上海": 100, "浙江": 200}}
t = pd.DataFrame(dic1,)
t.to_excel(writer,startrow=2,sheet_name="Sheet1",)

dic2= [{"隐患类型1": 1, "隐患类型2": 2},
      {"隐患类型1": 10, "隐患类型2": 20},
      {"隐患类型2": 100, "隐患类型3": 200}]

t = pd.DataFrame(dic2,index=["浙江2","上海2","北京2"])
t.to_excel(writer,startrow=8,sheet_name="Sheet1")


dic3= [
    [1,2,3,4,5],
    [7,8,9,10,11],
    [17,18,19,20,21],
]

t = pd.DataFrame(dic3,index=["浙江3","上海3","北京3"],columns=["数据3-1","数据3-2","数据3-3","数据3-4","数据3-5"])
t.to_excel(writer,startrow=15,sheet_name="Sheet1")

dic4={"a": [1, 2, 3,],
       "b": [4, 5, 6,]}

t = pd.DataFrame(dic4,index=["浙江4","上海4","北京4"])
t.to_excel(writer,startrow=22,sheet_name="Sheet1")
writer.save()

1.2 从excel csv txt文本读取也是返回一个DataFrame对象

2.1 从DataFrame对象中读取一个Series对象

查询列

print(d1["第一列"])
print(d1[["第一列","第二列"]])

查询行

print(d1.loc[1:]) 
print(d1.loc[:2])
print(d1.loc[0:4]) #和python语法不同,loc是闭区间.

类型转换
astype可以转换数据类型
df['B'] = df['B'].astype(int)

查询数据的方法

loc方法和iloc方法 详见下文

where和mask方法 where 替换条件为false处的值,mask相反,替换条件为true处的值,
where和mask方法是在DataFrame上调用的条件方法。它接受一个条件表达式作为参数,并将不符合条件的元素替换为指定的值。被替换的元素仍保留在DataFrame中,只是值变为指定的替换值。
df.where(cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False)
df.mask(cond, other=nan, inplace=False, axis=None, level=None, errors='raise', try_cast=False)

query方法:query方法是在DataFrame上调用的查询方法。它接受一个字符串参数,该字符串包含条件表达式,用于筛选DataFrame的行。query方法将返回满足条件的行。query方法的语法如下:
df.query(expr, inplace=False, **kwargs)
query方法用于筛选DataFrame的行,而where方法和mask方法用于条件替换元素。
query方法使用字符串表示条件表达式,可以使用DataFrame中的列名作为变量,并支持多个条件的组合。而where方法和mask方法的条件表达式使用布尔值或条件的函数表示。
query方法支持使用@符号引用外部变量,这在处理大型DataFrame时尤为有用。where方法和mask方法不支持此功能。
以下是一个代码示例,演示了where、mask和query方法的使用:

import pandas as pd
import numpy as np

# 创建DataFrame
df = pd.DataFrame({'A': [1, 2, 3, 4],
                   'B': [5, 6, 7, 8],
                   'C': [9, 10, 11, 12]})

# 使用where方法替换不满足条件的元素为0
df_where = df.where(df > 5, 0)

# 使用mask方法替换满足条件的元素为0
df_mask = df.mask(df > 5, 0)

# 使用query方法筛选满足条件的行
df_query = df.query('A > 2')

# 打印输出
print("df: ")
print(df)
print("\ndf_where: ")
print(df_where)
print("\ndf_mask: ")
print(df_mask)
print("\ndf_query: ")
print(df_query)


用读取csv,并且设置日期为首列为例

p = pd.read_csv(pth,encoding="gbk",)
p.set_index("日期",inplace=True)
print(p.head(10)) #数据如下

一. df.loc 通过标签索引字符串查询数据

在 Pandas 的 loc 中,可以使用不同的方式指定行索引和列索引,具体用法如下:

单个标签:使用单个标签来选择特定的行或列。例如:df.loc[row_label, col_label]

列表:使用列表来选择多个行或列。例如:df.loc[[row_label1, row_label2], [col_label1, col_label2]]

切片:使用切片表示范围选择多个连续的行或列。例如:`df.loc[row_label_start:row_label

1.1使用单个label查询数值 行或者列,传入单个值,精确匹配,得到一个值

print(p.loc["2011/3/1","天气情况"])     #>>>> 多云
#使用多个label查询数值 ,传入多个值,得到一个series
print(p.loc["2011/3/1",["天气情况","最高温度"]])

天气情况    多云
最高温度    5℃
Name: 2011/3/1, dtype: object

1.2使用多个值批量查询

print(p.loc[["2011/3/1","2011/3/3"],"天气情况"])

'''

日期
2011/3/1 多云
2011/3/3 晴
Name: 天气情况, dtype: object
'''

1.3使用值的区间查询

#行index按区间查询
print(p.loc["2011/3/1":"2011/3/3","天气情况"])

#列index按区间查询
print(p.loc["2011/3/1","最低温度":"天气情况":])

#行和列都按区间查询,返回一个DataFrame
print(p.loc["2011/3/1":"2011/3/3","最低温度":"天气情况":])

1.4使用条件表达式查询 找到天气情况为乌云的所有列表数据

print(p.loc[p["天气情况"] =="多云",:])
#多个条件用逻辑符号,每个条件用括号包裹进行过滤
print(p.loc[(p["天气情况"] =="多云") |( p["天气情况"] =="晴~多云"),:])

1.5使用函数形式进行条件查询

print(p.loc[lambda x:(x["天气情况"] =="多云") |( x["天气情况"] =="晴~多云"),:])

1.6使用自定义函数形式进行条件查询

#df默认index是日期,把日期强转字符串,过滤出2011年的天气晴的日期

def aa(df):
    return (df.index.str.startswith("2011")) & (df["天气情况"] =="晴")
print(p.loc[aa, :])

二. df.iloc 通过标签索引数字查询数据

iloc是通过索引获取数据,使用方法和loc雷同.
这里演示一个iloc实现的多条件过滤,代码如下
result = df[(df.iloc[:, 11].notnull()) & (df.iloc[:, 12].notnull())]
df.iloc[:, 11].notnull() 获取df中11列非空的数据,返回的是该列的bool值
2个条件用 & 拼接 则返回11列和12列都不为空的数据.

但是如果改成result = df[df.iloc[:, 2:3].notnull()]`` 意义就不同
df.iloc[:, 2:3].notnull() 这里的2:3是顾头不顾尾,所以返回第2行不为空的布尔值
最终返回的是所有的数据,因为索引是11列,所以这个表达式只会显示第2列的数据,如果第N行布尔值为真则显示数值,其余则显示NAN

在条件过滤时,我们通常需要使用 Series,因此需要使用单个整数索引来选择单个列。因此,对于过滤第11和第12列不为空的条件,可以使用 (df.iloc[:, 11].notnull()) & (df.iloc[:, 12].notnull()) 来判断。
这样,df.iloc[:, 11] 选择了第11列,返回一个 Series 对象,可以使用 notnull() 方法判断该列是否不为空。同样地,df.iloc[:, 12] 选择了第12列,并使用 notnull() 方法判断该列是否不为空。最后,使用 & 运算符将这两个条件组合起来,以得到一个布尔 Series,表示第6列和第7列都不为空的行。

3.1 数据新增的4中方法

1.直接赋值,我们来新增一列最高温度和最低温度差

让我们把下面的温度去除单位并改变类型为整数类型便于计算

p.loc[:,"最高温度"]=p.loc[:,"最高温度"].str.replace("℃","").astype("int")
p.loc[:,"最低温度"]=p.loc[:,"最低温度"].str.replace("℃","").astype("int")

然后新增温差列,这里就是2个Series列相减,并返回一个Series列

p.loc[:,"温差"]=p["最高温度"]-p["最低温度"]
print(p.head(10))

2.df.apply

apply处理的是一个Series列,并且用axis参数标注Index是行索引还是列索引,默认是axis是1为列索引,0是为行索引

比如添加一列做判断,如果最高温度>=10为高温,反之为低温

def wendu(x):
if x["最高温度"]>=10:
return "高温"
else:
return "低温"

p.loc[:,"最高温度"]=p.loc[:,"最高温度"].str.replace("℃","").astype("int")
p.loc[:,"最低温度"]=p.loc[:,"最低温度"].str.replace("℃","").astype("int")
p.loc[:,"高温低温判断"] = p.apply(wendu,axis=1)
print(p.head(10))
print(p["高温低温判断"].value_counts()) #对Series单列的数据进行聚合统计

'''高温 1038
低温 58
Name: 高温低温判断, dtype: int64'''

3.df.assgin 可以同时添加多个列到DateFrame,和apply不同,不用指定axis,而是直接指定列的索引

我们把最高温度和最低温度都加10,assign方法不会修改原数据,

p.loc[:,"最高温度"]=p.loc[:,"最高温度"].str.replace("℃","").astype("int")
p.loc[:,"最低温度"]=p.loc[:,"最低温度"].str.replace("℃","").astype("int")
p =p.assign(
#add_10 就是一个新的列名索引
add_10 = lambda x:x["最高温度"]+10,
add2_10 = lambda x:x["最低温度"]+10,
)
print(p.head(10))

4.按条件选择分组分别赋值

高低温差大于10度算温差大,先创建一个新列

p["温差大吗"]="" #pandas会用广播语法对列的每行都赋值为空值
p.loc[:,"最高温度"]=p.loc[:,"最高温度"].str.replace("℃","").astype("int")
p.loc[:,"最低温度"]=p.loc[:,"最低温度"].str.replace("℃","").astype("int")
p.loc[ p["最高温度"]- p["最低温度"]>10,"温差大吗"]="温差10度以上"
print(p.head(10))



### 4.1 pandas里的一些统计函数语法

p.loc[:,"最高温度"]=p.loc[:,"最高温度"].str.replace("℃","").astype("int")
p.loc[:,"最低温度"]=p.loc[:,"最低温度"].str.replace("℃","").astype("int")
print(p.head(10))

Pandas统计函数

1.汇总类统计 describe 有针对数字列的各种汇总的计算,mean 平均值 std标准差 ,min最小值 max最大值

print(p.describe())

1.1 也可以计算某一列的统计数据

print(p["最低温度"].mean(),p["最低温度"].std())

2.唯一去重和按值计算 对于非数字类型的数据 有unique()唯一性去重 value_counts()按值计数

print(p["天气情况"].unique())
print(p["天气情况"].value_counts())

3.相关系数和协方差 cov()协方差矩阵 corr()相关系数矩阵 ,针对数字类型的数据

print(p["最低温度"].cov(p["最高温度"])) #查看最低温度和最高温度的协方差矩阵,

3 pandas对于缺失值的处理

  • 对于这种excel,头2行为空,我们读取的时候可以用skiprows参数越过 p = pd.read_csv(pth,encoding="gbk",skiprows=2)

    输入p.isnull() 会对所有行和列进行空值检测,返回布尔,当然也可以对单列进行空值检测,p["分数"].isnull()

    或者用notnull()检测不为空的数据, p.loc[p["分数"].notnull(),:] # isnull和notnull 检测是否空值,用于Series和DataFrame
  • 接下来我们把第一列为空的数据丢弃掉
# dropna:丢弃 删除缺失值
# axis: 删除行或者列 {0 or "index",1 or "columns"} 默认为0
#
# how:如果等于any 任何空值都能删除,all代表所有值为空才删除
# subset: dropna() 按行操作(即删除行),但是你可以通过指定 subset 参数来告诉它只在特定的列中查找缺失值  df.dropna(subset=['A', 'C'], inplace=True) 查找a c列为空的行进行删除
# inplace, 布尔值,true 表示直接修改当前数据

p.dropna[axis="columns",how="all",inplace=True]

  • 接下来我们把每一行都为空的行给删除掉 p.dropna[axis="index",how="all",inplace=True]
  • 将分数列空值的填充为0分
# fillna 填充空值
value 用于填充的值,可以是单个值,或者字典,key为列名,value为值
method  ffill(forward fill)使用前一个不为空的值 bfill(backword fill)使用后一个不为空的值
axis: 按行或者列填充   {0 or "index",1 or "columns"} 默认为0
inplace, 布尔值,true 表示直接修改当前数据

p.fillna({"分数":0}) 或者 p.loc[:,"分数"]=p["分数"].fillna(0)

  • 将姓名列空值填充,这里根据规律的出用ffill方法
    p.loc[:,"姓名"]=p["姓名"].fillna(method = "ffill")
  • 保存 p.to_excel(pth,index=False) index=False 就是不要默认生成的index列

6.1 pandas的SettingWithCopyWarning的错误解决办法

官方文档解释https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy

pth = r"D:\杂项\py课程\pandas\test22.csv"
p = pd.read_csv(pth,encoding="gbk")

#pandas的SettingWithCopyWarning的错误解决办法
con = p["日期"].str.startswith("2011/3") # 设置过滤条件,取2011年3月份的数据
# p[con]["温差"] = 0 # 这里就会报错 SettingWithCopyWarning
'''
 SettingWithCopyWarning的原因根据官方文档解释 是因为p[con]["温差"] 是分成2个步骤的,等于p.get[con].set["温差"]
 第一步get获取到的数据是对DataFrame筛选后获取的子DataFrame对象,而这个子对象可能是copy,可能是view
 view指的是子对象,对子对象的更改直接影响原DataFrame的更改,而copy是对原DataFrame的一个备份,不影响原始数据.
 所以导致有时候成功有时候报错.
 解决办法思路:我们把copy还是view给他定义清楚,然后再做set赋值操作.
 一:用loc函数,loc函数是直接在原始DataFrame上操作,就是view操作
 二:对筛选内容复制一份,直接在复制的子对象上操作

'''
#解决办法1: 用loc方法
p.loc[con,"温差"] = 0
#解决办法2: 复制出一个子对象,所有的结果对原始数据不存在影响
p1 = p[con].copy()
p1["温差"]=0

7.1 pandas的数据排序功能

pth = r"D:\杂项\py课程\pandas\test22.csv"
p = pd.read_csv(pth,encoding="gbk")
p.loc[:,"最低温度"] = p["最低温度"].str.replace("℃","").astype(int)
p.loc[:,"最高温度"] = p["最高温度"].str.replace("℃","").astype(int)
## pandas数据排序
'''
Series排序:
Series.sort_values(ascending=True,inplace =True)

DataFrame.sort_values(by,ascending=True,inplace =True)
by:字符串或者列表,用于单列排序或者多列排序
或者通过by和ascending 2个参数使用列表多多列进行自定义排序
'''
#Series排序
# print(p["最高温度"].sort_values(ascending=False))
#DataFrame 排序 多列都按升序排序
#天气情况  最高温度都按升序排列
p.sort_values(by=["最高温度","天气情况"],ascending=True,inplace=True)
#天气情况 按升序排列  最高温度按降序排列
p.sort_values(by=["最高温度","天气情况"],ascending=[True,False],inplace=True)
print(p.head(50))

8.1 pandas字符串的处理方法

pandas的字符串是默认支持正则语法

p = pd.read_csv(pth,encoding="gbk")
p.loc[:,"最低温度"] = p["最低温度"].str.replace("℃","").astype(int)
p.loc[:,"最高温度"] = p["最高温度"].str.replace("℃","").astype(int)
'''
 pandas字符串处理
 1.先获取Series的str属性,然后在属性上调用相关函数
 2.只能使用于字符串数据,数字列不能用
 3.DataFrame上没有str属性和处理方法
 4.Series.str不是python原生的字符串,更多方法看官方文档https://pandas.pydata.org/docs/reference/series.html  搜索string handling
 5.str是Series属性,而silce等方法是str的方法,不可以用p["最低温度"].slice调用, 
    多个方法可以反复用str.方法调用 比如:p["最低温度"]str.slice[:,6].str.repleace("!",".")
'''

9.1 pandas axis轴概念的理解


但是对于跨行或者跨列轴的概念理解,就是指定了那个axis就按另一个axis方向进行遍历.

10.1 pandas 数据合并


例如下面这组数据

data1 = pd.DataFrame(
    {"姓名":["谢霆锋","张学友","梁静茹","谢霆锋","张学友","张韶涵"],
"歌曲": ["谢谢你的爱", "吻别", "勇气", "黄种人", "遥远的她","淋雨一直走"]}
)

data2 = pd.DataFrame(
    {"姓名":["谢霆锋","刘德华","梁咏琪","张韶涵"],
"歌曲": ["因为爱所以爱", "17岁", "花火", "快乐崇拜"]}
)


d1 = pd.DataFrame({'姓名': ['张三', '张三', '王五'],'班级': ['1班', '2班', '1班'],'分数': [30,20,10]})

d2 = pd.DataFrame({'姓名': ['张三', '张三', '王五','王五'],'班级': ['1班', '1班', '1班','2班'],'分数': [80,50,60,70]})
# 当然on也可以加入列表,表示2张表用多个索引进行关联
d3 = pd.merge(d1,d2,on=["姓名","班级"],how="inner")


那么对于不同的索引值要怎么处理呢???

d1 = pd.DataFrame({'姓名': ['张三', '张三', '王五'],'班级': ['1班', '2班', '1班'],'分数': [30,20,10]})
d2 = pd.DataFrame([[40,"1班"],[70,"2班"],[5,"2班"]],index=["张三","李四","王五"],columns=["分数","班级"])
# d2的姓名变成了默认索引,这种情况下我们可以用left_on指定用d1的姓名数据,然后开启d2的right_index为true,让2个索引进行关联
d3 = pd.merge(d1,d2,left_on="姓名",right_index=True,how="inner")

11.1 index的意义

index 包括行index或者列index,本身不是数据本体内容,是pandas添加上去的,主要是为了更快的查找数据.
如果index是唯一的,pandas会使用哈希优化表,查找性能为1次 p.index.is_unique #判断index是否有唯一性
如果index不是唯一,但是有序的,会通过二分法查找,查询性能为指数级下降. p.index.is_monotonic_increasing #判断index是否单调递增
如果index不是唯一且无序的,每次查找都要扫描所有数据,性能最低下.

  • 设置index 使用p.setindex("日期",inplace=True,drop=False),
    把日期设置为索引列,drop默认是true, 默认情况下,日期列设置了索引列,就从列中删除了.如果设置false,在列中依旧显示.效果如下

    例:平时我们查找2011年的数据,p.loc[p['日期']=="2011/3/1"],但是如果默认索引就是日期,那么p.loc["2011/3/1"] 查询结果一样,因为查询默认本身就是查索引

  • 不同对象,但是相同的索引值是可以相互进行计算的,比如:

a = pd.Series([1,2,3],index=["a","b","c"])
b = pd.Series([1,2,3],index=["d","b","f"])
print(a+b)  #结果只有b这个索引项会进行数值相加
  • index有更多的强大的数据结构支持, (了解)

12.1 数据的新增

数据的增加份两种,一种是在内存中新增,比如向之前的series或者dataframe新增数据,用append方法,
append不会对原数据进行修改,需要额外参数接收新增后的数据

df.append(self, other, ignore_index=False,verify_integrity=False, sort=False)

other 是它要追加的其他 DataFrame 或者类似序列内容
ignore_index 如果为 True 则重新进行自然索引
verify_integrity 如果为 True 则遇到重复索引内容时报错
sort 进行排序
  • 相同数据结构
    result = df1.append(df2)

  • 不同的数据结构
    不同结构的追加,没有的列会增加,没有对应内容的会为空
    result = df1.append(df4, sort=False)

  • 追加多个数据
    result = df1.append([df2, df3])

  • 列表形式追加

s2 = pd.Series(['X0', 'X1', 'X2', 'X3'],
               index=['A', 'B', 'C', 'D'])
result = df1.append(s2, ignore_index=True)

  • 字典形式追加
dicts = [{'A': 1, 'B': 2, 'C': 3, 'X': 4},
         {'A': 5, 'B': 6, 'C': 7, 'Y': 8}]
result = df1.append(dicts, ignore_index=True, sort=False)

12.2 另一种形式新增是对excel已存在的数据新增,将新数据写入到已存在的行数之后,这里也分两种方式

一种是读取excel的数据,然后用上面的append方法写入数据,
第二种是获取当前excel的数据下标,用loc方法新增多行或者一行数据,
pd.to_csv()的话就非常方便,直接使参数mode='a’即可,但是to_excel没有这个参数,所以只能从侧面来实现了。这里主要就是限制开始写入的行(startrow)

df= pd.DataFrame(pd.read_excel('test.xlsx')) #读取原数据
df_rows = df.shape[0] #获取行数
#增加一条数据
df.loc[df_rows] = [a1 , b2 , c3 , d4] #与原数据同格式
df.to_excel('test.xlsx', sheet_name='sheet1', index=False, header=True)

12.3 还有新增的形式是新增到另一张sheet表中

pandas中有2个类 ExcelWriter和ExcelFile,
ExcelFile()中的方法来获取目标表格文件的相关信息

#使用sheet_names来查看当前表格中包含的所有sheet名称(按顺序):
import pandas as pd
demo_excel = pd.ExcelFile(r'D:demo.xlsx')
print(demo_excel.sheet_names)

#使用parse()可以根据传入的sheet名称来提取对应的表格信息
table1 = demo_excel.parse(sheet_name=demo_excel.sheet_names[0])
print(table1.shape)

使用ExcelWriter()可以向同一个excel的不同sheet中写入对应的表格数据,新版的pandas的ExcelWriter类已经有了mode参数可以直接追加

writer = pd.ExcelWriter(path=pth,engine="openpyxl",mode="a")
df1.to_excel(writer,sheet_name='第一表',index=0)
df2.to_excel(writer,sheet_name='第二表',index=0) #index=0:无索引
writer.save()  # 不执行save就不会保存

------------------------------------------------------------------------------
#和openpyxl搭配使用新增数据
import pandas as pd
from openpyxl import load_workbook
result2=[('a','2','ss'),('b','2','33'),('c','4','bbb')]#需要新写入的数据
df = pd.DataFrame(result2,columns=['xuhao','id','name'])#列表数据转为数据框
df1 = pd.DataFrame(pd.read_excel('123.xlsx',sheet_name='aa')) #读取原数据文件和表
writer = pd.ExcelWriter('123.xlsx',engine='openpyxl')
book=load_workbook('123.xlsx')
writer.book = book
writer.sheets = dict((ws.title, ws) for ws in book.worksheets)
df_rows = df1.shape[0] #获取原数据的行数
df.to_excel(writer, sheet_name='aa',startrow=df_rows+1, index=False, header=False)#将数据写入excel中的aa表,从第一个空行开始写
writer.save()#保存

综合建议,如果需要同一个工作表进行添加的,也可以先用csv追加保存,然后整体转成xlsx.
如果是不同工作表追加的,直接使用ExcelWriter()类进行实现

遍历工作表

ExcelFile是一个类,可以用来获取工作簿里的工作表,sheet_names就是他工作表的表名属性,还可以用parse()方法对每个表进行操作,parse()方法和pd.read_excel()功能差不多
假设excel数据量庞大,一共有10个工作表,那么用read_excel()方法等于要重复打开10次excel,耗时较长.而用ExcelFile().parse 只需要打开一次,提高了效率.

import pandas as pd
pth=r"C:\Users\admin\Desktop\0601-专有云.xlsx"
table1=pd.ExcelFile(pth)
shts= table1.sheet_names
for i in shts:
    data = pd.read_excel(pth,sheet_name=i)
    #data=table1.parse(i)   parse()方法和pd.read_excel()功能差不多,都可以把表的数据返回成一个dataframe

考虑到read_excel()每调用一次,就会打开一次excel表格.如果一定要用这个函数,那么可以pd.io.excel.ExcelFile,然后把这个io代替read_excel里的pth代替传入

io = pd.io.excel.ExcelFile(path)
x1 = pd.ExcelFile(path)
all_name = x1.sheet_names
df = pd.read_excel(io, sheet_name=all_name)
# 操作每个sheet页
for sheet in all_name:
    df_new = df[sheet]

待准备

pandas过滤6和7列不为空为何要写成 (df.iloc[:, 6].notnull()) & (df.iloc[:, 7].notnull())
而不能写成(df.iloc[:, [6:7]].notnull())

在 Pandas 中,要使用 iloc 进行整数位置索引时,需要传递一个整数索引或索引的切片(例如 6:7)来选择列。使用 iloc[:, 6:7] 可以选择第6列到第7列之间的所有列,但这将返回一个 DataFrame,而不是一个 Series。

在条件过滤时,我们通常需要使用 Series,因此需要使用单个整数索引来选择单个列。因此,对于过滤第6和第7列不为空的条件,可以使用 (df.iloc[:, 6].notnull()) & (df.iloc[:, 7].notnull()) 来判断。

这样,df.iloc[:, 6] 选择了第6列,返回一个 Series 对象,可以使用 notnull() 方法判断该列是否不为空。同样地,df.iloc[:, 7] 选择了第7列,并使用 notnull() 方法判断该列是否不为空。最后,使用 & 运算符将这两个条件组合起来,以得到一个布尔 Series,表示第6列和第7列都不为空的行。

因此,为了正确地进行条件过滤,需要使用 (df.iloc[:, 6].notnull()) & (df.iloc[:, 7].notnull()),而不是 (df.iloc[:, 6:7].notnull())。

result = df[(df.iloc[:, 11].notnull()) & (df.iloc[:, 12].notnull())]
new_df = result.fillna(method='ffill',axis=0)
new_df.to_excel(new_pth,index=False)

pandas优化事项

在处理大型数据集时,Pandas提供了几种方法来帮助减少内存使用。以下是一些常用的技巧和最佳实践:

读取时指定数据类型:对于数值型列,可以指定一个比默认类型更小的数据类型。例如,如果你知道一个整数列的值范围在int8内,你可以指定它的数据类型为'int8'而不是默认的'int64'。

dtypes = {'Col1': 'int8', 'Col2': 'float32'}
df = pd.read_excel('path_to_excel_file.xlsx', dtype=dtypes)

使用更高效的数据类型:对于类别数据,如果你有一个包含大量重复值(低基数)的字符串列,使用category类型而不是object类型可以大幅节约内存。

df['Col'] = df['Col'].astype('category')

处理时分块读取:使用chunksize参数读取数据,在一次只处理一小块数据,而不是一次性读取整个文件到内存。

chunksize = 10000  # 或者任何适当的块大小
chunks = pd.read_excel('path_to_excel_file.xlsx', chunksize=chunksize)
for chunk in chunks:
    # 对每个块执行操作

优化字符串处理:对于大量文本数据,考虑外部处理。例如,字符串清理和转换可以在数据库查询中完成,或者使用更高效的库(如Dask或PySpark)。

只读取必要的列:如果可能,只载入分析时真正需要的列。

cols_to_use = ['Col1', 'Col2', 'Col3']
df = pd.read_excel('path_to_excel_file.xlsx', usecols=cols_to_use)

避免链式赋值:链式赋值(e.g., df = df[df['Col'] > 0])会导致整个DataFrame的复制。尽量用就地(inplace)操作来代替。

释放未使用内存:在移除了不再需要的数据后,可以使用gc.collect()强制进行垃圾回收,释放内存。

import gc
del df  # 删除不再需要的DataFrame
gc.collect()

使用迭代器:如果你需要对DataFrame中的数据进行逐个元素的操作,使用迭代器例如itertuples() 或 iterrows(),它们比直接对DataFrame进行迭代更为内存高效。

# 创建一个大型的 DataFrame
import pandas as pd

# 创建一个简单的DataFrame
df = pd.DataFrame({
    'A': [1, 2, 3],
    'B': [4, 5, 6],
    'C': [7, 8, 9]
})

# 使用 iterrows() 迭代行,每次迭代返回行标签和行数据(以Series形式)
for index, row in df.iterrows():
    # 可以访问每一列的值
    print(f'Row index: {index}')
    print(f'A: {row["A"]}, B: {row["B"]}, C: {row["C"]}')
    # 注意:这里的 row 是一个 Series 对象

# 使用 itertuples() 迭代行,每次迭代返回一个具名元组
for row in df.itertuples():
    # 可以访问每一列的值
    print(f'Row index: {row.Index}')  # Index属性来自DataFrame的索引
    print(f'A: {row.A}, B: {row.B}, C: {row.C}')
    # 注意:这里的 row 是一个具名元组

iterrows()和itertuples()之间的主要区别在于它们迭代时返回的对象类型。iterrows()返回一个包含行索引和行数据(Series对象)的元组,而itertuples()返回一个具名元组,这让整行的数据可以像访问属性那样方便。itertuples()通常比iterrows()更快,因为它返回的具名元组的访问速度比Series对象快。
在性能敏感的场景下,建议使用itertuples(),因为它通常提供更好的性能。但请注意,如果DataFrame中含有大量列,并且迭代过程中修改了原始的DataFrame数据,可能会导致错误的结果。这是因为iterrows()迭代时返回的是DataFrame每行的副本,而不是视图(view),所以对Series的修改不会反应到原始的DataFrame上。

posted @ 2021-08-24 22:56  零哭谷  阅读(319)  评论(0编辑  收藏  举报