python3之pandas库
Pandas 以 NumPy 为基础(实现数据存储和运算),提供了专门用于数据分析的类型、方法和函数,对数据分析和数据挖掘提供了很好的支持;同时 pandas 还可以跟数据可视化工具 matplotlib 很好的整合在一起,轻松实现数据可视化呈现。
Pandas 核心的数据类型是Series(数据系列)、DataFrame(数据窗/数据框),分别用于处理一维和二维的数据,除此之外,还有一个名为Index的类型及其子类型,它们为Series和DataFrame提供了索引功能。日常工作中DataFrame使用得最为广泛,因为二维的数据结构刚好可以对应有行有列的表格。Series和DataFrame都提供了大量的处理数据的方法,数据分析师以此为基础,可以实现对数据的筛选、合并、拼接、清洗、预处理、聚合、透视和可视化等各种操作。
Series
Pandas 库中的Series对象可以用来表示一维数据结构,但是多了索引和一些额外的功能。
Series类型的内部结构包含了两个数组,其中一个用来保存数据,另一个用来保存数据的索引。
# Pandas 库中的Series对象可以用来表示一维数据结构,但是多了索引和一些额外的功能。
# Series类型的内部结构包含了两个数组,其中一个用来保存数据,另一个用来保存数据的索引。
import pandas as pd
# 通过列表或数组创建Series对象
ser1 = pd.Series(data=[120, 380, 250, 360], index=["a", "b", "c", "d"])
# 通过字典创建Series对象
ser2 = pd.Series({"a": 320, "b": 180, "c": 300, "d": 405})
# 索引
print(ser1[0]) # 130 整数索引
print(ser1["a"]) # 130 自定义索引
print(ser1.iloc[0]) # 130 整数索引
print(ser1.loc["a"]) # 130 自定义索引
ser2[0] = 180
print(ser2["a"]) # 200
print(ser1[1:3]) # 切片索引
print(ser1["b":"d"])
print(ser1[["b", "d"]]) # 花式索引
print(ser1[ser1 >= 300]) # 布尔索引
# 属性
print(ser1.dtype) # 数据类型 int64
print(ser1.hasnans) # 有没有空值 False
print(ser1.index) # 索引 Index(['a', 'b', 'c', 'd'], dtype='object')
print(ser1.values) # 值 [200 180 300 405]
print(ser1.is_monotonic_increasing) # 是否单调递增 True
print(ser1.is_unique) # 是否每个值都独一无二 False
常用方法
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
ser1 = pd.Series(data=[120, 380, 250, 360], index=["a", "b", "c", "d"])
ser2 = pd.Series({"a": 320, "b": 180, "c": 300, "d": 405})
# 标量运算
ser1 += 10
# 矢量运算
ser3 = ser1 + ser2
# 统计相关方法
print(ser2.count()) # 计数
print(ser2.sum()) # 求和
print(ser2.mean()) # 求平均
print(ser2.median()) # 找中位数
print(ser2.max()) # 找最大
print(ser2.min()) # 找最小
print(ser2.std()) # 求标准差
print(ser2.var()) # 求方差
print(ser2.describe()) # 获得上述所有的描述性统计信息,返回Series对象
# 累积结果
print(ser1.cumsum()) # 逐个累加
print(ser1.cumprod()) # 逐个累乘
print(ser1.cummax()) # 逐个取最大值
print(ser1.cummin()) # 逐个取最小值
# 空值判断与处理
ser4 = pd.Series(data=[10, 20, np.nan, 30, None])
print(ser4.isna()) # 用于空值的判断
print(ser4.notna()) # 用于非空值的判断
print(ser4.dropna()) # 删除空值,inplace参数:默认值是False,表示删除空值不会修改原来的Series对象
print(ser4.fillna(value=5)) # 填充空值,inplace参数:默认值是False,表示填充空值不会修改原来的Series对象
print(ser4.fillna(method="ffill")) # 用空值前面的非空值填充
# 重复值判断与处理
print(ser2.duplicated()) # 检查数据是否重复
print(ser2.drop_duplicates()) # 删除重复数据
# 条件替换
ser5 = pd.Series(range(5))
print(ser5.where(ser5 > 0)) # 不满足条件的值进行替换成NaN
print(ser5.where(ser5 > 1, 10)) # 不满足条件的值进行替换成指定值
print(ser5.mask(ser5 > 1, 10)) # 满足条件的值进行替换成指定值
# 数据处理
ser6 = pd.Series(["a", "b", np.nan, "cc"])
print(ser6.map({"a": "aa", "b": "bb"})) # 通过字典给出的映射规则对数据进行处理,未在字典中的值将被映射成 NaN
print(ser6.map("value: {}".format, na_action="ignore")) # 通过指定的函数对数据进行处理
print(ser1.apply(lambda x: x - 100))
print(ser1.apply(lambda x, value: x - value, args=(5,))) # args参数,用于给lambda函数的第二个参数传值
# 排序
print(ser1.sort_values()) # 对数据按照从小到大排序
print(ser1.sort_index(ascending=False)) # 对索引按照从大到小排序
print(ser1.nlargest(2)) # 值最大的2个
print(ser1.nsmallest(1)) # 值最小的2个
# 配合matplotlib绘制图表
ser1.plot(kind="bar") # 通过plot方法的kind指定图表类型为柱状图
plt.ylim(0, 600) # 定制纵轴的取值范围
plt.xticks(rotation=0) # 定制横轴刻度(旋转到0度)
for i in range(ser1.size):
plt.text(i, ser1[i] + 5, ser1[i], ha="center") # 为柱子增加数据标签
# plt.show()
# 其它方法
print(ser2.unique()) # 获得由独一无二的值构成的数组 [180 300 405]
print(ser2.nunique()) # 统计不重复值的数量 3
print(ser2.value_counts()) # 统计每个值重复的次数,返回Series对象
print(ser2.mode()) # 获得数据的众数,返回Series对象
DataFrame
Pandas 库中的DataFrame对象可以用来表示二维数据结构,有行索引Index和列索引Columns。
DataFrame提供了极为丰富的属性和方法,实现对数据的重塑、清洗、预处理、透视、呈现等一系列操作。
import numpy as np
import pandas as pd
from sqlalchemy import create_engine, text
# 通过字典创建DataFrame对象
df1 = pd.DataFrame(
{
"名字": ["a", "b", "c", "d", "e"],
"出生日期": ["2002-05-11", "2004-06-02", "2004-07-18", "2004-08-02", "2002-09-23"],
"语文": [62, 72, 93, 88, 93],
"数学": [95, 65, 86, 66, 87],
"英语": [66, 75, 82, 69, 82],
}
)
print(df1)
print(df1.dtypes) # 每一列的数据类型,python字符串在DataFrame是object类型
print(df1.empty) # 是否为空 False
print(df1.ndim) # 维度 2
print(df1.shape) # 形状 (5, 5)
print(df1.size) # 元素的个数 25
print(df1.values) # 数据对应的二维数组
print(df1.index) # 行索引 RangeIndex(start=0, stop=5, step=1)
print(df1.columns) # 列索引 Index(['名字', '出生日期', '语文', '数学', '英语'], dtype='object')
# 通过二维数组创建DataFrame对象
df2 = pd.DataFrame(
[
["a", "b", "c", "d", "e"],
["2002-05-11", "2004-06-02", "2004-07-18", "2004-08-02", "2002-09-23"],
[62, 72, 93, 88, 93],
[95, 65, 86, 66, 87],
[66, 75, 82, 69, 82],
],
)
print(df2)
print(df2.index) # 行索引 RangeIndex(start=0, stop=5, step=1)
print(df2.columns) # RangeIndex(start=0, stop=5, step=1)
# 通过二维数组创建DataFrame对象,指定行列索引
scores = np.random.randint(60, 101, (5, 3))
courses = ["物理", "化学", "地理"]
stu_ids = np.arange(2001, 2006)
df3 = pd.DataFrame(data=scores, columns=["语文", "数学", "英语"], index=stu_ids)
print(df3)
print(df3.index) # 行索引 Int64Index([2001, 2002, 2003, 2004, 2005], dtype='int64')
print(df3.columns) # 列索引 Index(['语文', '数学', '英语'], dtype='object')
# 空DataFrame对象
df4 = pd.DataFrame()
print(df4)
print(df4.empty) # 是否为空 True
# 读取CSV文件创建DataFrame对象
# df5 = pd.read_csv("https://raw.githubusercontent.com/mwaskom/seaborn-data/master/tips.csv") # 读取远端csv文件
# print(df5)
df5 = pd.read_csv("../../seaborn-data-master/tips.csv") # 读取本地csv文件
print(df5)
print(df5.shape) # (244, 7)
print(df5.index) # RangeIndex(start=0, stop=244, step=1)
print(df5.columns) # Index(['total_bill', 'tip', 'sex', 'smoker', 'day', 'time', 'size'], dtype='object')
# 读取Excel工作表创建DataFrame对象
df6 = pd.read_excel("../../files/info.xls", sheet_name="Sheet1")
print(df6)
print(df6.shape) # (4, 5)
print(df6.index) # RangeIndex(start=0, stop=4, step=1)
print(df6.columns) # Index(['class', 'name', 'age', 'grade', 'dt'], dtype='object')
# 读取关系数据库二维表创建DataFrame对象
engine = create_engine("mysql+pymysql://root:@127.0.0.1:3306/test?charset=utf8mb4") # 通过指定的URL访问数据库
con = engine.connect()
df7 = pd.read_sql(text("select * from test"), con, index_col="id") # 根据SQL加载数据
df7 = pd.read_sql("test", con, index_col="id") # 加载全部数据
engine.connect().close() # 释放数据库连接
print(df7)
print(df7.shape) # (8, 3)
print(df7.index) # Int64Index([1, 6, 7, 8, 9, 10, 11, 12], dtype='int64', name='id')
print(df7.columns) # Index(['name', 'age', 'create_time'], dtype='object')
索引
import pandas as pd
df1 = pd.DataFrame(
{
"no": [20220101, 20220102, 20220103, 20220104, 20220105],
"名字": ["a", "b", "c", "d", "e"],
"语文": [62, 72, 93, 88, 93],
"数学": [95, 65, 86, 66, 87],
"英语": [66, 75, 82, 69, 82],
}
)
# 设置索引
df1 = df1.set_index("no")
# 索引
print(df1.at[20220101, "数学"]) # 自定义索引获取单个值 95
print(df1.iat[0, 2]) # 整数索引获取单个值 95
print(df1.loc[20220101]) # 自定义索引获取一行,返回Series对象
print(df1.iloc[0]) # 整数索引获取一行,返回Series对象
print(df1.iloc[0].values) # ['a' '二年级' '2002-05-11' 62 95 66]
print(df1.loc[20220101, "数学"]) # 自定义索引获取单个值 95
print(df1.iloc[0, 2]) # 整数索引获取单个值 95
print(df1.iloc[[1, 2]]) # 花式索引 获取多行
print(df1.loc[20220101:20220103]) # 切片
print(df1["数学"]) # 自定义索引获取一列,返回Series对象
print(df1["数学"].values) # 自定义索引获取一列 [95 65 86 66 87]
print(df1[["数学", "英语"]]) # 花式索引 获取多列
print(df1[(df1["数学"] > 85) & (df1["英语"] > 75)]) # 布尔索引
# 数据修改
df1.iloc[1, 1] = 80
print(df1.iloc[1, 1]) # 80
# 轴
df2 = df1[["名字", "语文", "数学", "英语"]]
df2.reset_index(inplace=True) # 重置索引,将索引列变成普通列
df2 = df2.drop(["no"], axis=1)
print(df2.mean(numeric_only=True)) # 按行的方向计算
print(df2.mean(numeric_only=True, axis=1)) # 按列的方向计算
数据预处理
import numpy as np
import pandas as pd
scores = np.random.randint(60, 101, (3, 3))
stu_ids = np.arange(2001, 2004)
df = pd.DataFrame(data=np.random.randint(60, 101, (3, 3)), columns=["语文", "数学", "英语"], index=stu_ids)
st_df1 = pd.DataFrame(data=np.random.randint(60, 101, (3, 3)), columns=["物理", "化学", "地理"], index=stu_ids)
st_df2 = pd.DataFrame(data=np.random.randint(60, 101, (4, 3)), columns=["语文", "数学", "英语"], index=np.arange(1001, 1005))
df1 = pd.DataFrame(
{
"no": [20220101, 20220102, 20220103, 20220104, 20220105],
"名字": ["a", "b", "c", "d", "e"],
"年级": ["二年级", "一年级", "一年级", "二年级", "二年级"],
"出生日期": ["2002-05-11", "2004-06-02", "2004-07-18", "2004-08-02", "2002-09-23"],
"语文": [62, 72, 93, 88, 93],
"数学": [95, 65, 86, 66, 87],
"英语": [66, 75, 82, 69, 82],
}
)
df2 = pd.DataFrame(
{
"地址": ["北京市,东城区", "北京市,东城区", "北京市,西城区", "北京市,海淀区", "北京市,海淀区"],
"价格": [62, 72, 93, 88, 93],
}
)
# 设置索引
df1 = df1.set_index("no")
# 查看各列所占内存大小
print(df.memory_usage(deep=True))
# DataFrame信息
print(df.info())
# 逆序
print(df.loc[::-1]) # 行逆序
print(df.loc[:, ::-1]) # 列逆序
# 数据行列转置
print(df.T)
# 更改列名
print(df.rename({"英语": "生物"}, axis=1))
# 列名替换
df.columns = df.columns.str.replace(" ", "_")
df.columns = df.columns.str.replace("[#@$]", "", regex=True)
# 列名前缀、后缀
print(df.add_prefix("1_"))
print(df.add_suffix("_1"))
# 类型转换
df1["年级"] = df1["年级"].astype("category") # 有限的可能取值可以归类为分类变量,可以提高数据处理和计算效率,并减少内存占用
df1["出生日期"] = pd.to_datetime(df1["出生日期"])
print(pd.to_numeric(df1["语文"], errors="coerce")) # 可转换的值正常转换,无法转换的值作为NaN
print(df1.apply(pd.to_numeric, errors="coerce")) # 全局转换
df1["语文"] = df1["语文"].astype("int8")
# 筛选最前或者最后指定数量数据
print(df.head()) # 获取最前面5行数据
print(df.head(2)) # 获取最前面2行数据
print(df.tail()) # 获取最后面5行数据
print(df.tail(2)) # 获取最后面2行数据
# 根据条件筛选数据
print(df.query("数学>85 and 英语>75"))
print(df2.query("地址.str.contains('东城')"))
print(df2.query("地址.str.contains('北京.*城区')"))
areas = ["北京市,东城区", "北京市,西城区"]
print(df2.query("地址==@areas"))
# 选择类型数据
print(df.select_dtypes(include="category"))
print(df.select_dtypes(include="number"))
print(df.select_dtypes(include=["category", "datetime"]))
print(df.select_dtypes(exclude="category"))
# 增加行数据
print(pd.concat([df, st_df2])) # 行合并
# 增加列数据
df.insert(3, "生物", [100, 95, 98])
print(pd.concat([df, st_df1], axis=1)) # 列合并
# 数据合并
# how:设置合并的方式,有inner,outer,left,right四种方式;on:依据那个列来合并
print(pd.merge(df, st_df1, how="left", right_index=True, left_index=True)) # 列数据合并
print(df.join(st_df1, how="left"))
# 缺失值
print(df.isnull()) # 找出数据表中的缺失值
print(df.isna()) # 找出数据表中的缺失值
print(df.isna().sum()) # 统计缺失值
print(df.dropna(inplace=False)) # 删除整行
print(df.dropna(axis=1, inplace=False)) # 删除整列
print(df.dropna(how="all")) # how="all" 所有数据列都是NaN时才删除
print(df.dropna(how="any")) # how="any" 只要有一个数据列是NaN,就会删除当前行数据
print(df.dropna(subset=["数学", "英语"])) # 具体指定哪些列为NaN时才删除数据
print(df.fillna(value=60)) # 缺失值填充为指定值
print(df.fillna(method="ffill")) # 缺失值填充为前值
print(df.interpolate()) # 插值法填补缺失值
# 重复值
print(df.duplicated("数学")) # 找出数据表中的重复值,返回布尔类型的Series对象
print(df.drop_duplicates("数学")) # 删除重复值,可以指定keep参数为first、last、False设定保留第一个、最后一个或者不保留重复数据
# 数据去噪
print(df["数学"].rolling(window=3).mean()) # 移动平均法:通过计算滑动窗口内的均值平滑数据
print(df[(df["数学"] > df["数学"].quantile(0.01)) & (df["数学"] < df["数学"].quantile(0.99))]) # 分位数过滤去除极端值
# 删除数据
print(df.drop(2001)) # 删除行
print(df.drop(df[(df["数学"] > 90) | (df["数学"] < 70)].index, axis=0)) # 删除行
print(df.drop(["语文", "数学"], axis=1)) # 删除列
# 数据替换
avg_sal = np.mean(df["语文"]).astype(int)
print(avg_sal)
print(df.replace(101, avg_sal)) # 替换
print(df.replace([101, -1], avg_sal))
# 数据采样
print(df1.sample(frac=0.5, random_state=1111)) # 按百分比采样,frac:样本数量占总量的百分比,random_state:随机状态,这个值相同,取出的样本是一样的
print(df1.sample(n=3, random_state=1111)) # 按个数采样
# 调整category类型的顺序
g_type = pd.CategoricalDtype(categories=["一年级", "二年级"], ordered=True)
df1["年级"] = df1["年级"].astype(g_type)
df1 = df1.sort_values("年级")
print(df1)
# 按范围转换catagory类型
df3 = pd.DataFrame(np.random.randint(1, 80, (20, 1)))
df3.columns = ["年龄"]
df3["年龄段"] = pd.cut(df3["年龄"], bins=[0, 18, 25, 60, 80], labels=["儿童", "青年", "成人", "老人"])
# 将分组统计后数据按原索引列出
df1["年级数学平均分"] = df1.groupby("年级").数学.transform("mean")
print(df1)
# 映射列值
print(df1["年级"].map({"一年级": 1, "二年级": 2}))
print(
df1["年级"].factorize()[0]
) # factorize函数返回的是二元元组,第一个元素是映射之后的数字数组,第二个元素是索引类型,索引的值就是列中各个不同的值。
print((df1["语文"] > 60).astype("int")) # 映射二值化
# 列拆分
df1["地址"] = ["北京市,东城区", "北京市,东城区", "北京市,西城区", "北京市,海淀区", "北京市,海淀区"]
df1["城市"] = df1["地址"].str.split(",", expand=True)[0]
df1[["市", "区"]] = df1["地址"].str.split(",", expand=True)
# 列上下移动
df1["地理"] = df1["语文"].shift(2) # 向下移动2行,,空出的行用NaN值填充
df1["生物"] = df1["语文"].shift(-2) # 向上移动2行,,空出的行用NaN值填充
# 逐行计算累积
df1["语文分数累加"] = df1["语文"].cumsum()
df1["语文分数累积最高"] = df1["语文"].cummax()
print(df1)
数据计算与呈现
import pandas as pd
import matplotlib.pyplot as plt
df1 = pd.DataFrame(
{
"no": [20220101, 20220102, 20220103, 20220104, 20220105],
"语文": [62, 72, 93, 88, 93],
"数学": [95, 65, 86, 66, 87],
"英语": [66, 75, 82, 69, 82],
}
)
df1 = df1.set_index("no")
df2 = pd.read_csv("../../seaborn-data-master/tips.csv")
df3 = pd.read_csv("../../seaborn-data-master/diamonds.csv")
# 解决中文乱码问题
plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei"] # 中文字体
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示为方块的问题
# 获取描述性统计信息
print(df1.describe()) # 获取每门课程的描述性统计信息
print(df1.max()) # 计算每门课程最高分,返回Series对象
print(df1.min()) # 计算每门课程最低分
print(df1.mean()) # 计算每门课程平均分
print(df1.std()) # 计算每门课程分数标准差
print(df1.var()) # 计算每门课程分数方差
print(df1.mean(axis=1)) # 计算每个学生平均分
# 排序
print(df1.sort_index(ascending=False)) # 索引降序
print(df1.sort_values(by="语文", ascending=False))
print(df1.sort_values(by=["语文", "数学"], ascending=False))
print(df1.nlargest(3, "语文"))
print(df1.nsmallest(3, "数学"))
# 排名
df1["英语排名"] = df1["英语"].rank(method="first", ascending=False) # 如同排序
df1["英语排名"] = df1["英语排名"].astype(int)
print(df1.sort_values("英语排名"))
df1["英语排名"] = df1["英语"].rank(method="min", ascending=False) # 并列名次存在时,后续的名次会跳跃。
df1["英语排名"] = df1["英语排名"].astype(int)
print(df1.sort_values("英语排名"))
df1["英语排名"] = df1["英语"].rank(method="dense", ascending=False) # 并列名次存在时,后续的名次仍然依次排下去
df1["英语排名"] = df1["英语排名"].astype(int)
print(df1.sort_values("英语排名"))
# 分组聚合
print(df2.groupby("time").total_bill.sum())
print(df2.groupby("time").total_bill.agg(["sum", "mean", "median", "max", "min", "count", "std", "var"])) # 多个聚合函数
print(df2.groupby("time").total_bill.agg(总额="sum", 最高="max", 最低="min", 平均="mean")) # 自定义聚合后的列的名字
print(df2.groupby("time")[["total_bill", "tip"]].agg({"tip": "sum", "total_bill": ["sum", "max", "min"]})) # 对多个列使用不同的聚合函数
# 透视表
print(pd.pivot_table(df2, index="time", values="tip", aggfunc="sum"))
print(pd.pivot_table(df2, index="day", values=["tip", "total_bill"], aggfunc="sum"))
print(pd.pivot_table(df2, index="time", columns="sex", values="tip", aggfunc="sum"))
print(pd.pivot_table(df2, index="day", columns="time", values="tip", aggfunc="sum", fill_value=0)) # 空值填充
print(pd.pivot_table(df2, index="day", columns="time", values="tip", aggfunc="sum", fill_value=0, margins=True, margins_name="总计")) # 总计
print(pd.crosstab(index=df2["day"], columns=df2["time"], values=df2["tip"], aggfunc="sum").fillna(0)) # 交叉表
tb = pd.pivot_table(df2, index="day", values=["tip", "total_bill"], aggfunc="sum") # 绘制图表
tb.plot(figsize=(8, 4), kind="bar")
# 计算环比
tb = pd.pivot_table(df2, index="day", values=["tip", "total_bill"], aggfunc="sum")
print(tb.pct_change())
# 相关性判定
print(df3[["carat", "depth", "table", "price"]].corr(numeric_only=True)) # 计算相关系数
# 绘制图表
df1.index = df1.index.astype(str)
df1.loc[:, :].plot(kind="line", title="成绩", rot=45)
df2.groupby(by=["day", "time"]).sum(numeric_only=True).plot(kind="bar", rot=45)
plt.show()
日期时间处理
import pandas as pd
df = pd.DataFrame(
{
"名字": ["aa", "bb", "cc"],
"出生日期": ["2002/5/11", "2004-06-02", "3/5/2002"],
"城市": ["北京", "上海", "广州"],
"成绩": [78, 81, 87],
"费用": [780, 810.5, 874.2],
},
)
# 日期格式处理
df["出生日期"] = pd.to_datetime(df["出生日期"]) # 统一处理成datetime64类型
print(df)
# 日期属性
print(df["出生日期"].dt.year)
print(df["出生日期"].dt.month)
print(df["出生日期"].dt.day)
print(df["出生日期"].dt.isocalendar().week) # 全年的第几周
print(df["出生日期"].dt.quarter) # 季度
# 日期序列
df = pd.DataFrame()
df["年"] = pd.date_range("2023-01-01", periods=3, freq="Y") # 年
df["月"] = pd.date_range("2023-01-01", periods=3, freq="M") # 月
df["日"] = pd.date_range("2023-01-01", periods=3, freq="D") # 日
df["周"] = pd.date_range("2023-01-01", periods=3, freq="W") # 周
df["季度"] = pd.date_range("2023-01-01", periods=3, freq="Q") # 季度
print(df)
# 日期调整
df = pd.DataFrame()
d = pd.date_range("2023-01-01", periods=3, freq="D")
df["原始日期"] = d
df["延迟三天"] = d.shift(3, freq="D")
df["提前三天"] = d.shift(-3, freq="D")
print(df)
数据导出存储
import pandas as pd
df = pd.read_csv("../../seaborn-data-master/tips.csv")
# csv
# 最常用格式,通用性强,文本编辑器可直接查看。
# 缺点:不支持数据类型存储,大文件读写效率低。
df.to_csv("../../files/test.csv", index=None)
# excel
# 支持多工作表,适合办公协作场景。
# 缺点:文件较大,读写速度慢,不适合超大数据集。
df.to_excel("../../files/test.xlsx", index=None)
print(pd.read_excel("../../files/test.xlsx"))
# Pickle
# Pickle格式是一种用于序列化和反序列化Python对象结构的二进制格式。它的主要特点是能够将程序中运行的对象信息保存到文件中。
# Python专属序列化格式,读写极快,保留数据类型。
# 缺点:磁盘占用较大,跨语言兼容性差,Python版本升级可能不兼容。
df.to_pickle("../../files/test.pkl")
print(pd.read_pickle("../../files/test.pkl"))
# Parquet
# Parquet格式是一种列式存储格式,被广泛应用于大数据处理领域。它采用了压缩和编码技术,能够有效地存储和压缩数据,同时保持数据的结构和模式。
# 列式存储,压缩率高,适合大数据(尤其结构化数据)。支持多语言,常用于Spark/Hadoop生态。
# 磁盘占用低,读取性能好,写入性能略差
# 依赖于pyarrow或者fastparquet库 pip install pyarrow
df.to_parquet("../../files/test.parquet")
print(pd.read_parquet("../../files/test.parquet"))
# Feather
# 轻量级二进制格式,读写速度快,磁盘占用高。
# 适合Pandas/R间快速临时交换数据。
# 依赖于pyarrow库 pip install pyarrow
df.to_feather("../../files/test.feather")
print(pd.read_feather("../../files/test.feather"))
定制EXCEL输出格式
# 定制excel输出格式
# 依赖于库 styleframe: pip install styleframe
import pandas as pd
from styleframe import StyleFrame, Styler, utils
df = pd.read_csv("../../seaborn-data-master/tips.csv")
df1 = pd.read_csv("../../seaborn-data-master/diamonds.csv")
# 内容自适应
style = Styler(shrink_to_fit=True)
sf = StyleFrame(df, styler_obj=style)
sf1 = StyleFrame(df1, styler_obj=style)
# 设置列宽
sf.set_column_width_dict({"total_bill": 25, "smoker": 20})
# 设置表头样式
header_style = Styler(
bg_color="silver",
bold=True,
font_size=12,
horizontal_alignment=utils.horizontal_alignments.center,
vertical_alignment=utils.vertical_alignments.center,
)
sf.apply_headers_style(header_style)
# 设置内容样式
content_style = Styler(
shrink_to_fit=True,
font_size=8,
horizontal_alignment=utils.horizontal_alignments.left,
)
sf.apply_column_style(sf.columns, content_style)
# 设置特定行的内容样式
row_style = Styler(
bg_color="#32CD32",
font_color="blue",
border_type=utils.borders.thick,
font="Microsoft YaHei",
bold=True,
font_size=8,
shrink_to_fit=True,
horizontal_alignment=utils.horizontal_alignments.left,
)
sf.apply_style_by_indexes([2, 4, 5], styler_obj=row_style)
# 设置列的数据样式
num_style = Styler(number_format=utils.number_formats.general_float)
sf.apply_column_style(["total_bill", "tip"], num_style)
writer = sf.to_excel("../../files/test_style.xlsx", index=None)
writer.close()
# 使用 ExcelWriter 导出多工作表,配合styleframe需指定使用openpyxl导出
with pd.ExcelWriter("../../files/test_style1.xlsx", engine="openpyxl") as writer:
sf.to_excel(writer, sheet_name="tips", index=None)
sf1.to_excel(writer, sheet_name="diamonds", index=None)
索引
import pandas as pd
import numpy as np
# 范围索引:是由具有单调性的整数构成的索引
index = pd.RangeIndex(1, 13, name="月份")
print(index) # RangeIndex(start=1, stop=13, step=1, name='月份')
ser1 = pd.Series(data=np.random.randint(60, 100, 12), index=index)
print(ser1)
# 分类索引:是由定类尺度构成的索引。常用于分类聚合。可以给索引指定一个顺序,分组聚合的结果会按照这个指定的顺序进行呈现
index = pd.CategoricalIndex(data=["香蕉", "苹果", "苹果", "桃子", "香蕉"], categories=["苹果", "香蕉", "桃子"], ordered=True)
print(index) # CategoricalIndex(['香蕉', '苹果', '苹果', '桃子', '香蕉'], categories=['苹果', '香蕉', '桃子'], ordered=True, dtype='category')
ser1 = pd.Series(data=np.random.randint(0, 10, 5), index=index)
print(ser1.groupby(level=0).sum()) # 苹果 4 香蕉 8 桃子 3
ser1.index = ser1.index.reorder_categories(["香蕉", "桃子", "苹果"])
print(ser1.groupby(level=0).sum()) # 香蕉 8 桃子 3 苹果 4
ser1.index = ser1.index.add_categories("梨")
print(ser1.groupby(level=0).sum()) # 香蕉 8 桃子 3 苹果 4 梨 0
# 多级索引
tuples = [(1, "red"), (1, "blue"), (2, "red"), (2, "blue")]
index = pd.MultiIndex.from_tuples(tuples, names=["no", "color"])
ser1 = pd.Series(data=np.random.randint(1, 10, 4), index=index)
print(index) # MultiIndex([(1, 'red'),(1, 'blue'),(2, 'red'),(2, 'blue')], names=['no', 'color'])
print(ser1.groupby("no").sum())
print(ser1.groupby(level=0).sum())
print(ser1.groupby(level=1).sum())
arrays = [[1, 1, 2, 2], ["red", "blue", "red", "blue"]]
index = pd.MultiIndex.from_arrays(arrays, names=["no", "color"])
print(index) # MultiIndex([(1, 'red'),(1, 'blue'),(2, 'red'),(2, 'blue')], names=['no', 'color'])
stu_ids = np.arange(1001, 1006)
cates = ["期中", "期末"]
index = pd.MultiIndex.from_product((stu_ids, cates), names=["学号", "学期"])
courses = ["语文", "数学", "英语"]
scores = np.random.randint(60, 101, (10, 3))
df = pd.DataFrame(data=scores, columns=courses, index=index)
print(df)
print(df.groupby(level=0).agg(lambda x: x.values[0] * 0.25 + x.values[1] * 0.75))
print(df.groupby(level=0).mean())
df = pd.DataFrame(
{
"名字": ["aa", "aa", "aa", "bb", "bb", "bb", "cc", "cc", "cc"],
"科目": ["语文", "数学", "英语", "语文", "数学", "英语", "语文", "数学", "英语"],
"成绩": [78, 81, 87, 87, 65, 85, 75, 85, 86],
},
)
# 建立多级索引
df = df.set_index(["名字", "科目"]).sort_index() # 通过多级索引,将行列数据转换为树形结构,表示数据的多维结构
print(df.loc["aa", :].loc["语文", :].loc["成绩"])
# 间隔索引:使用固定的间隔范围充当索引
index = pd.interval_range(start=0, end=5)
print(index) # IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]], dtype='interval[int64, right]')
print(index.contains(1.5)) # 检查范围内是否包含了某个元素 [False True False False False]
print(index.overlaps(pd.Interval(1.5, 3.5))) # 检查一个范围跟其他的范围是否有重叠 [False True True True False]
index = pd.interval_range(start=pd.Timestamp("2022-01-01"), end=pd.Timestamp("2022-01-04"), closed="both") # 指定closed包含start和end值
print(index) # IntervalIndex([[2022-01-01, 2022-01-02], [2022-01-02, 2022-01-03], [2022-01-03, 2022-01-04]], dtype='interval[datetime64[ns], both]')
# 日期时间索引
index = pd.date_range("2024-1-1", "2024-6-30", periods=3) # 指定生成数量
print(index) # DatetimeIndex(['2024-01-01 00:00:00', '2024-03-31 12:00:00','2024-06-30 00:00:00'], dtype='datetime64[ns]', freq=None)
index = pd.date_range("2021-1-1", "2021-6-30", freq="W-MON") # 指定采样周期
print(index)
dates = pd.date_range("2025-01-01", periods=3, freq="D")
print(dates) # DatetimeIndex(['2025-01-01', '2025-01-02', '2025-01-03'],dtype='datetime64[ns]', freq='D')
df = pd.DataFrame({"value": np.random.randn(3), "category": ["A", "B", "C"]}, index=dates)
print(df["2025-01-01":"2025-01-02"]) # 时间切片
print(df.asfreq("2D")) # 指定频率抽样
print(df.resample("2D").mean(numeric_only=True)) # 重采样
print(df.resample("2D").agg(["mean", "max"], numeric_only=True)) # 重采样
print(df.index + pd.Timedelta(days=1)) # 时间偏移计算 DatetimeIndex(['2025-01-02', '2025-01-03', '2025-01-04'], dtype='datetime64[ns]', freq='D')
print(df.index.year) # 属性访问 Int64Index([2025, 2025, 2025], dtype='int64')
print(df.index.dayofweek) # 属性访问 Int64Index([2, 3, 4], dtype='int64')
print(df.tz_localize("Asia/Chongqing")) # 日期时间本地化
print(df.tz_localize("Asia/Chongqing").tz_convert("America/New_York")) # 对时间本地化以后转换时区
浙公网安备 33010602011771号