没看过基础教程的请先去看基础教程,不然零帧起手容易看不懂:
还在用Pandas?Polars!这篇就够了,2.5万字+详解!
一、DataFrame的属性和方法
1、属性
属性 | 描述 |
---|---|
columns | 获取或者设置表格的列名 |
dtypes | 获取各列的数据类型 |
flags | 我也不懂(苦笑) |
height | 获取表格行数 |
schema | 获取表格架构,返回类似于字典的mapping类型 |
shape | 获取表格形状 |
width | 获取表格列数 |
import polars as pl
创建初始表格
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6, 7, 8],
"ham": ["a", "b", "c"],
}
)
print("columns是",df.columns,'\n','------------------')
print("dtypes是",df.dtypes,'\n','------------------')
print("flags是",df.flags,'\n','------------------')
print("height是",df.height,'\n','------------------')
print("schema是",df.schema,'\n','------------------')
print("shape是",df.shape,'\n','------------------')
print("width是",df.width,'\n','------------------')
2、聚合方法
方法 | 描述 |
---|---|
count() | 按列计数非空值 |
max() | 各列的最大值 |
max_horizontal() | 每行最大值 |
mean() | 每列平均数 |
mean_horizontal() | 每行平均值 |
median() | 每列中位数 |
min() | 每列最小值 |
min_horizontal() | 每行最小值 |
product() | 每列各自所有的值相乘的结果 |
quantile() | 每列特定分位数 |
std() | 每列标准差 |
sum() | 每列求和 |
sum_horizontal() | 获取每行求和 |
var() | 每列方差 |
创建初始表格
import polars as pl
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6, 7, 8],
}
)
min,max,meaan,sum后面加上_horizontal就能转化为按行求和这里就展示一个例子剩下的类推就可。std和median直接省略
print('min','\n',df.min(),'\n')
print('min\_horizontal','\n',df.min_horizontal(),'\n') 注意horizontal返回的是Series
product
print('median','\n',df.product(),'\n')
quantile print('median','\n',df.quantile(quantile=0.5,interpolation='nearest'),'\n') 这个方法有两个参数:
1、quantile:分位数,数值为0-1
2、interpolation: 要求的分位数在数据中的两个数之间,这个时候该怎么选择, 可以选{‘nearest’, ‘higher’, ‘lower’, ‘midpoint’, ‘linear’}之一
sum_horizontal 这各有点特殊
print('median','\n',df.sum_horizontal(ignore_nulls=True),'\n')
sum_horizontal有一个参数: ignore_nulls,接受布尔值,如果射程False,那么只要行里有空值,该行的运算结果就是空值
3、表格运算
1、fold() 对数据表按行进行reduction,方式是函数
参数:
operation :立即执行函数
返回值:Series
按行拼接
df = pl.DataFrame(
{
"a": ["foo", "bar", None],
"b": [1, 2, 3],
"c": [1.0, 2.0, 3.0],
}
)
print('按行拼接','\n',df.fold(operation=lambda s1,s2:s1 + s2),'\n')
按行合并布尔值(我也不知道有什么用,看看得了)
df = pl.DataFrame(
{
"a": [False, False, True],
"b": [False, True, False],
}
)
print('按行合并布尔值','\n',df.fold(operation=lambda s1,s2:s1 | s2),'\n')
4、描述性方法
注意 :前面加‘ * ’的方法有参数
方法 | 描述 |
---|---|
*describe() | 展示表格的统计数据,类似pandas的describe |
*estimated_size() | 返回表格占用的内存大小 |
*glimpse() | 简略地展示表格 |
is_duplicated() | 检测是否是重复行,返回布尔值 |
is_empty() | 监测表格是否为空(用的不多) |
is_unique() | 检测是否是唯一行,和前面的is_duplicated相反 |
*n_unique() | 返回唯一行的数量(唯一行就是不重复行) |
null_count() | 返回表格各列空值数量 |
先讲没有参数的 初始数据集
import polars as pl
df = pl.DataFrame(
{
"foo": [1, None, 3,None,1],
"bar": [6, 7, None,1,6],
"ham": ["a", "b", "c","a","a"],
}
)
is_duplicated()
print('is\_duplicated()','\n',df.is_duplicated(),'\n')
is_empty()
print('is\_empty()','\n',df.is_empty(),'\n')
is_unique()
print('is\_unique()','\n',df.is_unique(),'\n')
null_count()
print('null\_count()','\n',df.null_count(),'\n')
describe()
有两个参数
percentiles :要取的分位数,数值在0-1之间,列表形式
interpolation :要求的分位数在数据中的两个数之间,这个时候该怎么选择,可以选{‘nearest’, ‘higher’, ‘lower’, ‘midpoint’, ‘linear’}之一
返回一个表格
from datetime import date, time
df = pl.DataFrame(
{
"float": [1.0, 2.8, 3.0],
"int": [40, 50, None],
"bool": [True, False, True],
"str": ["zz", "xx", "yy"],
"date": [date(2020, 1, 1), date(2021, 7, 5), date(2022, 12, 31)],
"time": [time(10, 20, 30), time(14, 45, 50), time(23, 15, 10)],
}
)
print('这是默认形式','\n',df.describe()) 这是默认形式
with pl.Config(tbl_rows=12): 这一行作用是让行全部显示不然结果会有省略,不用记。可以看到传参后的分位数变成了我们想要的样子
print('传参后','\n',df.describe(percentiles=[0.1, 0.3, 0.5, 0.7, 0.9],interpolation="linear",))
estimated_size()有一个参数:
**单位** : {‘b’, ‘kb’, ‘mb’, ‘gb’, ‘tb’} 这些是表示空间大小的单位从byte到TB
estimated_size() 数据表还是看上面那个 print(df.estimated_size(unit='b')) 结果表明这个表格一共占了92b内存
glimpse()有三个参数,都不太重要,想了解的问AI
数据表还是看上面那个 print(df.glimpse())
n_unique()有一个参数:
subset :哪些列参与到n_unique运算,默认全部列
返回数字
df = pl.DataFrame(
{
"a": [1, 1, 2, 3, 4, 5],
"b": [0.5, 0.5, 1.0, 2.0, 3.0, 3.0],
"c": [True, True, True, False, True, True],
}
)
print(df.n_unique(subset=["b", "c"]))
5、转换Dataframe表格为其他形式
方法有很多,这里只写最主要的,其他的确实没什么学习意义
方法 | 描述 |
---|---|
to_dict() | 转化成字典 |
to_dicts() | 转化成类似于struct的形式 |
to_numpy() | 转化成numpy数组 |
to_pandas() | 转化成pandas表格 |
to_struct() | 转化成struct |
to_dict()和to_dicts()区别:
to_dicts()没有参数
to_dict()有一个参数:
as_series :接受布尔值,如果为False,返回值是字典,如果为True返回值是Series,默认为True
创建初始表格
df = pl.DataFrame(
{
"A": [1, 2, 3, 4, 5],
"fruits": ["banana", "banana", "apple", "apple", "banana"],
}
)
to_dicts()
print('to\_dicts()','\n',df.to_dicts(),'\n')
to_dict()
print('to\_dict()无参数','\n',df.to_dict(),'\n')
print('to\_dict()有参数','\n',df.to_dict(as_series=False),'\n')
很显然我们平时一般用df.to_dict(as_series=False)
to_numpy()有五个参数,试了一下都没什么用
to_numpy() 数据用上面的 import numpy as np print(df.to_numpy())
to_struct()其实跟to_dicts()很像,有一个参数:
name :接受str,为struct取名
df = pl.DataFrame(
{
"a": [1, 2, 3, 4, 5],
"b": ["one", "two", "three", "four", "five"],
})
print(df.to_struct("nums"))
6、groupby分组
**分组后每组都看成Dataframe表格就行了**
groupby之后的数据结构:每个组其实是用(name.data)这样的元组表示的。name是分组列的各个唯一值,data是该唯一值对应的表格部分数据,看下面:
df = pl.DataFrame({"foo": ["a", "a", "b"], "bar": [1, 2, 3]})
for i in df.group_by("foo"):
print(i) 结果发货了两个元组,每个元组的第一个值是个子元组比如('b',),这就是name,每个元组第二个值是个表格记录了该组数据
下面看分组后的方法,很多和Dataframe方法是一样的:
方法 | 描述 |
---|---|
agg() | 这是最重要的,对每个组的的列进行操作 |
all() | 展示groupby对象的所有内容 |
count() | 每组行数 |
first() | 每组首个值 |
head() | 每组前n个值,比如.head(3)表示显示前三个 |
last() | 每组最后一个值 |
len() | 跟count()一样 |
map_groups() | 把每组当成Dataframe表格执行特定函数 |
max() | 每组最大值 |
min() | 每组最小值 |
median() | 每组中位数 |
n_unique() | 每组唯一值数量 |
quantile() | 获取特定的分位数,这个跟Dataframe的quantile()一样 |
sum() | 每组和 |
tail() | 每组最后n个值,比如.head(n)表示显示前三个 |
count,first,last,len,head,max,min,median,quantile,n_unique,tail,sum,mean都很容易理解只举一个例子
df = pl.DataFrame(
{
"a": [1, 2, 2, 3, 4, 5],
"b": [0.5, 0.5, 4, 10, 13, 14],
"c": [True, True, True, False, False, True],
"d": ["Apple", "Orange", "Apple", "Apple", "Banana", "Banana"],
}
)
print(df.head(2))
agg是重点,因为groupby的目的就是使用agg
df = pl.DataFrame(
{
"a": ["a", "b", "a", "b", "c"],
"b": [1, 2, 1, 3, 3],
"c": [5, 4, 3, 2, 1],
}
)
print(df.group_by("a").agg(pl.col("b"), pl.col("c"))) 基础篇说过分组后各列执行reduction成为了List
典型例子
df1=df.group_by("a").agg(
b_sum=pl.sum("b"),
c_mean_squared=(pl.col("c") ** 2).mean(),
)
print(df1)
map_groups()对每组执行函数,这个感觉用的不会太多
df = pl.DataFrame(
{
"id": [0, 1, 2, 3, 4],
"color": ["red", "green", "green", "red", "red"],
"shape": ["square", "triangle", "square", "triangle", "square"],
}
)
df1=df.group_by("color").map_groups(
lambda group_df: group_df.sample(2)
)
print(df1)
7、运算与选择
这节方法真的超多,为了保护肝脏,我就把平时几乎用不到的给省略了,避免过度学习,实在想学就去看官方文档吧
方法 | 描述 |
---|---|
Dataframe[rows,columns] | 跟pandas一样,选择特定行列或者特定值 |
cast() | 把整个表格转化数据类型 |
clone() | 复制表格,相当于pandas的copy() |
drop() | 删除某些列 |
drop_nulls() | 删除存在空值的行 |
explode() | 把一个List类型的列竖向摊开,得到一个长表格 |
extend() | 类似于竖向concat |
fill_nan() | 填充NaN值 |
fill_null() | 填充空值 |
filter() | 最基本的上下文不用再说了吧 |
gather_every() | 按特定间隔取行 |
group_by_dynamic() | 基础篇提到过,这个方法主要是用来进行时间窗口分析的 |
insert_column() | 往特定位置插入一个新列 |
interpolate() | 对空值进行插值 |
join() | 像SQL那样横向连接两个表格 |
join_where() | 按给定条件拼接,两个表格行数可能不一样 |
join_asof() | 跟日期相关的join,这个不讲了,确实太少用了,问ai吧 |
pivot() | 行转列 |
rename() | 重命名列 |
sample() | 对行进行抽样 |
select() | 最基本的上下文不用再说了吧 |
tail() | 选最后n行 |
to_dummies() | 对某些列进行独热编码,类似于pandas的get_dummies() |
transpose() | 转置表格 |
with_row_index() | 给表格添加索引列 |
unique() | 删除重复列 |
unpivot() | 列转行 |
Dataframe[rows,columns]再简单提一嘴吧 df[0, 'a']选择第0行a列数据 df[0]选择第0行数据 df['a']选择第a列数据 df[0:2]选择第0到2行的数据 df[0:2, 'a']选择第0到2行a列的数据 df[0:2, 0]选择第0到2行第0列数据 df[[0, 1], [0, 1, 2]]选择第0,1行的第0,1,2列数据
cast其实一般给单独的列用,这里是对整个数据表用,有两个参数:
dtype :要转化成什么格式
strict :接受布尔类型,若为True那么当转换遇到问题时会报错,否则不会。默认为True
from datetime import date
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6.0, 7.0, 8.0],
"ham": [date(2020, 1, 2), date(2021, 3, 4), date(2022, 5, 6)],
}
)
print(df)
print(df.cast({"foo": pl.Float32, "bar": pl.UInt8}))
clone没什么好讲的,就是复制原表格的方法,使用时直接df1=df.clone()就行了
drop有两个参数:
columns :一般用列表传递要drop的列,之所以前面加‘’是要提醒你使用drop的时候不要‘df.drop(columns=[?])’,这个columns不算形参,使用它会报错。正确例子看下面代码
strict :接受布尔类型,如果为True验证当前模式中是否存在所有列名,如果不存在,则抛出异常。默认为True
返回的是个新表格,不会修改原来的表格
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"foo": [6.0, 7.0, 8.0],
"ham": ["a", "b", "c"],
}
)
print(df.drop("ham"))
print(df.drop(['foo','foo'],strict=True))
drop_nulls()有一个参数:
subset : 哪些列参与到函数运算中,接受列表,默认全表
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6, None, 8],
"ham": ["a", "b", None],
}
)
print(df.drop_nulls(['foo','bar'])) 只有foo,bar列有空值的行才会被删除
explode()有一个参数:
columns :哪些列会被explode,接受单个列名,多个列名则用List包裹传入
df = pl.DataFrame(
{
"letters": ["a", "a", "b", "c"],
"numbers": [[1], [2, 3], [4, 5], [6, 7, 8]],
"numbers111": [[1], [2, 0], [9, 5], [3, 6, 1]],
}
)
print(df)
print(df.explode(["numbers","numbers111"]))
extend有一个参数:
other :另一个表格
df1 = pl.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})
df2 = pl.DataFrame({"foo": [10, 20, 30], "bar": [40, 50, 60]})
print(df1.extend(df2))
fill_nan()有一个参数:
value :用什么值填充fill_nan值
df = pl.DataFrame(
{
"a": [1.5, 2, float("nan"), 4],
"b": [0.5, 4, float("nan"), 13],
}
)
print(df.fill_nan(99))
fill_null()有四个参数,这里只写两个能用到的:
value :以什么值填充
strategy :用什么策略来填充空值,可选{None, ‘forward’, ‘backward’, ‘min’, ‘max’, ‘mean’, ‘zero’, ‘one’}
注意value,和strategy是互斥的,两者只能传一个
df = pl.DataFrame(
{
"a": [1, 2, None, 4],
"b": [0.5, 4, None, 13],
}
)
df.fill_null(99) 用单个值
df.fill_null(strategy="forward") 用strategy
gather_every()有两个参数:
n :每过n行抽取一行
offset :从什么位置开始,即开始行的索引
import polars as pl
s = pl.DataFrame({"a": [1, 2, 3, 4], "b": [5, 6, 7, 8]})
print(s.gather_every(2, offset=1))
group_by_dynamic()比较复杂,有一堆参数,这里讲主要的:
index_column :分组的datetype列
every :窗口之间的时间间隔,接受字符串和timedelta类型
period :窗口的时间长度,如果不赋值,那么默认和every相同,接受字符串和timedelta类型
offset :设置分组开始的起点,接受字符串和timedelta类型
include_boundaries :是否给表格添加每个窗口的下边时间,和上边时间两列,接受布尔值。
closed :窗口的哪个边是封闭的,{‘left’, ‘right’, ‘both’, ‘none’}可选。分别表示左,右,全封闭和都不封闭
start_by :定义决定窗口起点的方式,可选{‘window’, ‘monday’, ‘tuesday’, ‘wednesday’, ‘thursday’, ‘friday’, ‘saturday’, ‘sunday’},window就是默认值,后面的几个只有在every包含‘w’(即week)时使用。表达的意思是周几作为第一个窗口的起点,且时间在第一个数据点之前,举例:‘Monday’表示周一作为第一个窗口起点,且这个周一是数据第一个时间点之前的周一,这个周一并不在数据表中,是虚构出来的。
group_by :还按哪个列分组
注意:进行分组的时间列必须先进行升序排序,不然会产生问题
可选的datetype值
datetype值 | 描述 |
---|---|
s | 秒 |
m | 分钟 |
h | 小时 |
d | 天 |
w | 周 |
mo | 月 |
q | 季度 |
y | 年 |
i | 表格的索引 |
3d12h4m25s | 还可以混合着写 |
创建数据表
from datetime import datetime
df = pl.DataFrame(
{
"time": pl.datetime_range(
start=datetime(2021, 12, 16),
end=datetime(2021, 12, 16, 3),
interval="30m",
eager=True,
),
"n": range(7),
}
)
df
典型示例
df1=df.group_by_dynamic("time", every="1h", closed="right").agg(pl.col("n")) closed="right"之后原来数据表中的第一个点变成了第一个窗口右边
print(df1)
df2=df.group_by_dynamic("time", every="1h", include_boundaries=True, closed="right").agg(pl.col("n").mean()) 从boundary能看出窗口上边和下边
print(df2)
df3=df.group_by_dynamic("time", every="1h", closed="left").agg(pl.col("n"))
print(df3)
df4=df.group_by_dynamic("time", every="1h", closed="both").agg(pl.col("n")) closed="both"之后两个相邻窗口有重合部分
print(df4)
df = df.with_columns(groups=pl.Series(["a", "a", "a", "b", "b", "a", "a"]))
df5=df.group_by_dynamic("time",every="1h",closed="both",group_by="groups",include_boundaries=True,).agg(pl.col("n")) 再按groups分组
print(df)
insert_columns()有两个参数:
index :列索引,在这个索引处插入新列,原来该处的列将后移
column :要插入的列
df = pl.DataFrame({"foo": [1, 2, 3], "bar": [4, 5, 6]})
s = pl.Series("baz", [97, 98, 99])
print(df.insert_column(index=1,column=s))
interpolate()没有参数,默认的插值方式就只有线性插值,这个有点拉。
df = pl.DataFrame(
{
"foo": [1, None, 9, 10],
"bar": [6, 7, 9, None],
"baz": [1, None, None, 9],
}
)
print(df.interpolate())
join有一堆参数:
other :另一个表格
on :按哪个列来join,使用on的条件是两个表格主键名一样
how :怎么join,可选{‘inner’, ‘left’, ‘right’, ‘full’, ‘semi’, ‘anti’, ‘cross’}这个基础篇讲过
left_on :如果两个表格主键名称不一样的时候,左表的主键名,这个参数必须与‘right_on’同时出现,且不能与‘on’同时出现。
right_on :如果两个表格主键名称不一样的时候,右表的主键名,这个参数必须与‘left_on’同时出现,且不能与‘on’同时出现。
suffix :为了区分合并后有相同名字列,给重复名字列加的后缀名
validate :验证两表连接时的对应方式,可选{‘m:m’, ‘m:1’, ‘1:m’, ‘1:1’},m表示many(多)
join_nulls :是否对空值也进行join,接受布尔类型,默认是不False
coalesce :是否合并两列主键可选None和布尔值,默认为None。注意如果主键不是列而是表达式时(polars可以这么干)这个参数没法用
创建数据表
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6.0, 7.0, 8.0],
"ham": ["a", "b", "c"],
}
)
other_df = pl.DataFrame(
{
"apple": ["x", "y", "z"],
"ham": ["a", "b", "d"],
}
)
df,other_df
df1=df.join(other_df, on="ham", how="full")
print(df1)
df2=df.join(other_df, on="ham", how="left", coalesce=True)
print(df2)
df3=df.join(other_df, on="ham", how="semi")
print(df3)
df4=df.join(other_df, on="ham", how="anti")
print(df4)
join_where()有三个参数:
other :另一个表格
predicates :判定join的条件,这个参数前面加‘’了,表示可以传入任意数量判定条件,且使用时不能以‘predicates=?’的形式传参
suffix :为了区分合并后有相同名字列,给重复名字列加的后缀名
east = pl.DataFrame(
{
"id": [100, 101, 102],
"dur": [120, 140, 160],
"rev": [12, 14, 16],
"cores": [2, 8, 4],
}
)
west = pl.DataFrame(
{
"t\_id": [404, 498, 676, 742],
"time": [90, 130, 150, 170],
"cost": [9, 13, 15, 16],
"cores": [4, 2, 1, 4],
}
)
df=east.join_where(west,pl.col("dur") < pl.col("time"),pl.col("rev") < pl.col("cost"),)
print(df)
pivot()有一堆参数:
on :接受列名,该列的所有唯一值会被转化为新的列
index :接受列名,该列将被作为索引列,列的值变为唯一值
values :接受列名,列内的值成为新表的value值,如果传入多列,请看下面的示例
aggregate_function :聚合函数,当一个匹配组 有多个值的时候该怎么reduction,可{‘min’,‘max’, ‘first’, ‘last’, ‘sum’, ‘mean’, ‘median’, ‘len’},也可以自定义函数表达式,用pl.element()代替每个匹配组的值
maintain_order :是否对index列排序使输出是可预测的,接受布尔值
sort_columns :是否对转换出的新列按名称进行 排序,接受布尔值,默认为False,即按照发现他们的顺序。
separator :后缀名前的分隔符,接受str类型
df = pl.DataFrame(
{
"name": ["Cady", "Cady", "Karen", "Karen"],
"subject": ["maths", "physics", "maths", "physics"],
"test\_1": [98, 99, 61, 58],
"test\_2": [100, 100, 60, 60],
}
)
df.pivot("subject", index="name", values="test\_1"),df.pivot("subject", index="name", values=["test\_1","test\_2"])
上面的写法将两个表达式并排写了单纯为了简便
df = pl.DataFrame(
{
"ix": [1, 1, 2, 2, 1, 2],
"col": ["a", "a", "a", "a", "b", "b"],
"foo": [0, 1, 2, 2, 7, 1],
"bar": [0, 2, 0, 0, 9, 4],
}
)
print(df)
print(df.pivot("col", index="ix", aggregate_function="sum")) 没传递value默认为剩下的两列对每个匹配组使用sum聚合
df = pl.DataFrame(
{
"col1": ["a", "a", "a", "b", "b", "b"],
"col2": ["x", "x", "x", "x", "y", "y"],
"col3": [6, 7, 3, 2, 5, 7],
}
)
df.pivot("col2",index="col1",values="col3",aggregate_function=pl.element().tanh().mean(),) 自定义函数
rename有两个参数:
mapping : 一个map映射或者函数,把旧列名变成新列名
strict :接受布尔值,是否验证当前架构中是否只存在表格列名,如果有异常,则引发异常。默认为True
df = pl.DataFrame(
{"foo": [1, 2, 3], "bar": [6, 7, 8], "ham": ["a", "b", "c"]}
)
df,df.rename({"foo": "apple",'as':'re'},strict=False),df.rename({"foo": "apple",'as':'re'},strict=False)
三个表并排写一起了,在第三个表中我们写了个不存在的as列,若此时strict为True那么会报错
df.rename(lambda column_name: "c" + column_name[1:]) 也可以用函数
sample()有五个参数:
n :要抽样多少行,不能和fraction并存
fraction :要抽取多少比例,0-1之间,不能和n并存
with_replacement :接受布尔值,是否是有放回抽样
shuffle :如果设置为 True,则将打乱样本行的顺序。如果设置为 False (默认值) ,返回行的顺序将既不保持原来顺序也不完全随机。
seed :接受Int类型,就跟我的世界的地图种子差不多,相同的种子会产生相同抽样结果
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6, 7, 8],
"ham": ["a", "b", "c"],
}
)
df.sample(n=2, seed=0)
to_dummies()有三个参数:
columns :要进行独热编码的列
separator :接受‘str’,用什么符号分隔列名与类别名,默认是‘_’
drop_first :接受布尔值,是否删掉已经独热编码的第一个类别列,默认为false不删
df = pl.DataFrame(
{
"foo": [1, 2],
"bar": [3, 4],
"ham": ["a", "b"],
}
)
df.to_dummies(), df.to_dummies(drop_first=True), df.to_dummies(drop_first=True, separator=":")
with_row_index()有两个参数:
name :index列名称
offset :起始值
df = pl.DataFrame(
{
"a": [1, 3, 5],
"b": [2, 4, 6],
}
)
print(df)
print(df.with_row_index(name='asd'))
unique()有三个参数:
subset :按照哪个或者哪几个判定重复
keep :对于判定有重复的行,应该保留哪个,可选{‘first’, ‘last’, ‘any’, ‘none’},any表示随机留下一个,none表示一个不留
maintain_order :接受布尔值,保持与原始 DataFrame 相同的顺序
注意:如果把list类型的列加入到判定当中会报错
df = pl.DataFrame(
{
"foo": [1, 2, 3, 1],
"bar": ["a", "a", "a", "a"],
"ham": ["b", "b", "b", "b"],
}
)
df.unique(maintain_order=True),df.unique(subset=["bar", "ham"], maintain_order=True),df.unique(keep="last", maintain_order=True)
unpivot()有四个参数:
on :把哪些或者哪个列转化成行的值
index :把那些列或者哪些行作为index列(实际上就是保持不变)
variable_name :被执行列转成行的原来的列名列名会组成一个新的列,你可以给它取名,默认是variable
value_name :被执行列转成行的原来的列值会组成一个新的列,可以取名,默认是value
df = pl.DataFrame(
{
"a": ["x", "y", "z"],
"b": [1, 3, 5],
"c": [2, 4, 6],
}
)
import polars.selectors as cs
df.unpivot(cs.numeric(), index="a",value_name='123',variable_name='321') 数字类型的列b,c被进行列转行
8、杂项
虽然是杂项也很重要。
方法 | 描述 |
---|---|
collect_schema() | 得到表格架构 |
corr() | 返回皮尔逊相关系数表 |
lazy() | 将Dataframe转换成Lazyframe |
map_rows() | 遍历每行执行函数 |
collect_schema()
df = pl.DataFrame(
{
"foo": [1, 2, 3],
"bar": [6.0, 7.0, 8.0],
"ham": ["a", "b", "c"],
}
)
df.collect_schema()
corr()
df = pl.DataFrame({"foo": [1, 2, 3], "bar": [3, 2, 1], "ham": [7, 8, 9]})
df.corr()
map_rows()不是在本地API,所以运行速度不会比pandas的apply(axis=1)快多少
有三个参数:
function :要执行的函数
return_dtype :操作返回的数据类型
df = pl.DataFrame({"foo": [1, 2, 3], "bar": [-1, 5, 8]})
df.map_rows(lambda t: (t[0] * 2, t[1] * 3)),df.map_rows(lambda t: (t[0] * 2 + t[1]))
Dataframe的相关内容就到这里了,我讲的很细,保证你一遍看懂,我以后复习的时候也得看这个,算是做了个笔记吧,顺便造福一下各位学友。
恭喜,你已经学完了Dataframe的属性和方法。把Lazyframe看成Dataframe就行了
什么是expression? 你就把expression当成表格的列就行了,只不过这个列不一定是原来的列,也可能是列表达式(对列进行处理的代码串)
1、聚合
方法 | 描述 |
---|---|
all() | 该列是否全为True |
any() | 该列是否全有True |
arg_max() | 该列最大值的索引 |
arg_min() | 该列最小值的索引 |
count() | 该列一共有多少非空值 |
first() | 该列第一个值 |
implode() | 把该列所有值缩成List |
last() | 该列的最后一个值 |
len() | 该列长度 |
max() | 该列最大值 |
mean() | 该列平均值 |
median() | 该列中位数 |
n_unique() | 该列唯一值数量 |
null_count() | 该列多少空值 |
quantile() | 得到特定分位数 |
std() | 该列标准差 |
sum() | 该列和 |
var() | 该列方差 |
product() | 该列连乘值 |
很多方法以前遇到过类似的就不讲了。只讲一些新的。
all(),any()都有一个参数:
ignore_nulls :接受布尔值,是否忽略空值,默认为True
df = pl.DataFrame(
{
"a": [True, True],
"b": [False, True],
"c": [None, True],
}
)
df.select(pl.col("*").all()),df.select(pl.col("*").any()) pl.col("*")表示选中所有列
arg_max(),arg_min()
df = pl.DataFrame(
{
"a": [20, 10, 30],
}
)
df.select(pl.col("a").arg_max()), df.select(pl.col("a").arg_min())
first(),last()
df = pl.DataFrame({"a": [1, 1, 2]})
df.select(pl.col("a").first()) ,df.select(pl.col("a").last())
implode()
df = pl.DataFrame(
{
"a": [1, 2, 3],
"b": [4, 5, 6],
}
)
df, df.select(pl.all().implode())
len()
df = pl.DataFrame({"a": [1, 2, 3], "b": [None, 4, 4]})
df.select(pl.all().len())
2、布尔值判定
下面的方法均返回布尔值Series,一般用在filter筛选中
方法 | 描述 |
---|---|
all() | 该列是否全为True |
any() | 该列是否全有True |
has_nulls() | 该列是否有空值 |
is_between() | 该列的值是否在给定区间内 |
is_duplicated() | 该列的值是否是重复出现的 |
is_first_distinct() | 该列的该值是否是第一次出现 |
is_last_distinct() | 该列的该值是否是最后一次出现 |
is_in() | 该列的该值是否在其他列的相应位置中出现 |
is_nan() | 该列的值是否是NaN |
is_not_nan() | 该列的值是否不是NaN |
is_not_null() | 该列的值是否不是null |
is_null() | 该列的值是否是null |
is_unique() | 该列的值是否只出现了一次 |
has_nulls()
df = pl.DataFrame(
{
"a": [None, 1, None],
"b": [10, None, 300],
"c": [350, 650, 850],
}
)
df.select(pl.all().has_nulls())
is_between()
lower_bound :下限
upper_bound :上限
closed :区间闭合状态,可选{‘both’, ‘left’, ‘right’, ‘none’},默认both
df = pl.DataFrame({"num": [1, 2, 3, 4, 5]})
df.with_columns(pl.col("num").is_between(2, 4).alias("is\_between")) 也能用于时间区间判断
is_duplicated()和is_unique()相反
df = pl.DataFrame({"a": [1, 1, 2]})
df.select(pl.col("a").is_duplicated())
is_first_distinct(),相当于pandas的unique()
df = pl.DataFrame({"a": [1, 1, 2, 3, 2]})
df.with_columns(pl.col("a").is_first_distinct().alias("first")), df.filter(pl.col("a").is_first_distinct().alias("first"))
两个表格并排写了,后面的表格filter搭配is_first_distinct()实现了类似pandas的unique功能
is_in()
df = pl.DataFrame(
{"sets": [[1, 2, 3], [1, 2], [9, 10]], "optional\_members": [1, 2, 3]}
)
df.with_columns(contains=pl.col("optional\_members").is_in("sets"))
is_nan() , is_not_nan()恰好相反
df = pl.DataFrame(
{
"a": [1, 2, None, 1, 5],
"b": [1.0, 2.0, float("nan"), 1.0, 5.0],
}
)
df.with_columns(pl.col(pl.Float64).is_nan().name.suffix("\_isnan"))
is_null() , is_not_null()恰好相反
df = pl.DataFrame(
{
"a": [1, 2, None, 1, 5],
"b": [1.0, 2.0, float("nan"), 1.0, 5.0],
}
)
df.with_columns(pl.all().is_null().name.suffix("\_isnull")) nan != null
3、类别API
类别API就一个方法:Expr.cat.get_categories(),只能对categorical和Enum使用,以得到该列唯一值
df = pl.Series(
"cats", ["foo", "bar", "foo", "foo", "ham"], dtype=pl.Categorical
).to_frame()
df.select(pl.col("cats").cat.get_categories())
4、关于列和列名
方法 | 描述 |
---|---|
alias() | 给列起别名 |
exclude() | 排除某些列 |
pl.col() | 这个不用讲了吧 |
exclude()有一个参数:
columns :哪个或哪些列被排除,多列可以用List传参。
df = pl.DataFrame(
{
"aa": [1, 2, 3],
"ba": ["a", "b", None],
"cc": [None, 2.5, 1.5],
}
)
df.select(pl.all().exclude("ba"))
5、运算
这一节主要介绍对列进行数学运算的方法
方法 | 描述 |
---|---|
abs() | 该列的绝对值 |
arccos() | 该列arccos值 |
arcsin() | 略 |
cbrt() | 求立方根 |
cos() | 略 |
cot() | 略 |
mode() | 该列众数 |
n_unique() | 唯一值的数量,包括null |
pct_change() | 本行相对于上面第n行的变化率 |
rank() | 定等级 |
sin() | 略 |
sqrt() | 开平方 |
tan() | 略 |
unique() | 返回该列的唯一值 |
value_counts() | 返回struct,每个唯一值出现的次数 |
这一节很简单,只讲点重要的
n_unique()
import polars as pl
df = pl.DataFrame({"x": [1, 1, 2, 2, 3], "y": [1, 1, 1, None, None]})
df.select(x_unique=pl.col("x").n_unique(),y_unique=pl.col("y").n_unique(),)
pct_change()有一个参数:
n : 每隔n行取一个值
df = pl.DataFrame(
{
"a": [10, 11, 12, None, 12, 15, 9,10]
}
)
df.with_columns(pl.col("a").pct_change().alias("pct\_change")), df.with_columns(pl.col("a").pct_change(2).alias("pct\_change"))
rank()有三个参数:
method :按什么方式进行rank,可选{‘average’, ‘min’, ‘max’, ‘dense’, ‘ordinal’, ‘random’}
descending :是否降序rank默认为False升序
seed :如果method="random",这个当种子,接受int类型,不过random用的也不多
初始数据集
import polars as pl
df = pl.DataFrame({"a": [3, 6, 1, 1, 6, 2, 3, 5]})
method="average"如果有重复值那么排名将是平均值
print('average','是\n',df.select(pl.col("a").rank("average")))
method="min"如果有重复值那么排名将是最小值
print('min','是\n',df.select(pl.col("a").rank("min")))
method="dense"重复值排名相同,不同的值按大小顺序,排名连续出现不会出现跳值。一般用这个
print('dense','是\n',df.select(pl.col("a").rank("dense")))
method="ordinal"及时相同的值排名也不同,而是按照出现的先后顺序进行rank的排名连续出现不会出现跳值
print('ordinal','是\n',df.select(pl.col("a").rank("ordinal")))
unique()有一个参数:
maintain_order :保持数据出现的顺序,但这需要更多性能
df = pl.DataFrame({"a": [1, 1, 2, 3, 1, 6]})
df.select(pl.col("a").unique())
value_counts()有四个参数,但能用到的就两个:
sort :接受布尔值,是否按出现次数降序排序各个唯一值,默认为False
normalize :接受布尔值,是否把结果中的唯一值出现次数换成相对比例
df = pl.DataFrame(
{"color": ["red", "blue", "red", "green", "blue", "blue"]}
)
print(df.select(pl.col("color").value_counts()))
print(df.select(pl.col("color").value_counts(sort=True)))
print(df.select(pl.col("color").value_counts(normalize=True)))
6、List列表
list方法很多但是都很简单而且容易记,从文档一路看下来的你肯定不费吹灰之力就能掌握,对列表达式用list方法时要这样用:expr.list.function()
方法 | 描述 |
---|---|
all() | 该list是否全为True |
any() | 略 |
argmax() | 列表最大值的索引 |
argmin() | 略 |
concat() | 按行合并两列list |
contains() | 列表中是否含指定内容 |
drop_nulls() | 去除list的空值 |
eval() | 对list执行expression表达式 |
explode() | 把list的值转化成行 |
first() | list第一个值 |
gather() | 获取list特定位置的值 |
gather_every() | 每隔n个数取一个 |
get() | 按照索引获取list数据 |
head() | 取list前n个数据,比如head(3) |
join() | 把list数据合并起来 |
last() | 获取列表最后一个值 |
len() | 获取列表长度 |
max() | 最大值 |
mean() | 平均值 |
median() | 中位数 |
min() | 最小值 |
n_unique() | 获取list中唯一值的数量 |
reverse() | 翻转list |
slice() | 获取list切片 |
sort() | 排序list |
std() | 标准差 |
sum() | 和 |
tail() | 获取list最后n个数据 |
unique() | 返回list的唯一值 |
var() | 方差 |
concat()
import polars as pl
df = pl.DataFrame(
{
"a": [["a"], ["x"]],
"b": [["b", "c"], ["y", "z"]],
}
)
df.with_columns(concat=pl.col("a").list.concat("b"))
contains()有一个参数:
item :要检查的内容,可以是float | str | bool | int | date | datetime | time |等类型,适用范围挺广的
df = pl.DataFrame({"a": [[3, 2, 1], [], [1, 2]]})
df.with_columns(contains=pl.col("a").list.contains(1))
drop_nulls()
df = pl.DataFrame({"values": [[None, 1, None, 2], [None], [3, 4]]})
df.with_columns(drop_nulls=pl.col("values").list.drop_nulls())
eval()
df = pl.DataFrame({"a": [1, 8, 3], "b": [4, 5, 2]})
df.with_columns(
rank=pl.concat_list("a", "b").list.eval(pl.element().rank()) 以前说过,pl.element()代表该list的所有值
)
explode()
df = pl.DataFrame({"a": [[1, 2, 3], [4, 5, 6]],'b':['A','B']})
df.select(pl.col("a").list.explode())
gather()有两个参数:
indices :接受列、int、Series、list。要取的值的索引。
null_on_oob :接受布尔值。如果索引超出范围,是报错还是设为null,默认False报错
df = pl.DataFrame({"a": [[3, 2, 1], [], [1, 2, 3, 4, 5]]})
df.with_columns(gather=pl.col("a").list.gather([0, 4], null_on_oob=True))
gather_every()有两个参数:
n :接受int类型,每隔多少数取一个
offset :接受int类型,规定起始点
df = pl.DataFrame(
{
"a": [[1, 2, 3, 4, 5], [6, 7, 8], [9, 10, 11, 12]],
"n": [2, 1, 3],
"offset": [0, 1, 0],
}
)
df.with_columns(
gather_every=pl.col("a").list.gather_every(
n=pl.col("n"), offset=pl.col("offset")
)
)
get()有两个参数:
index :要取的值的索引
null_on_oob :接受布尔值。如果索引超出范围,是报错还是设为null,默认False报错
df = pl.DataFrame({"a": [[3, 2, 1], [], [1, 2]]})
df.with_columns(get=pl.col("a").list.get(0, null_on_oob=True)) 注意:get方法只能取一个值,不像gather一样可以取多个所以你为什么不用gather呢
join()有两个参数:
separator :合并后不同的值用什么符号做间隔
ignore_nulls :接受布尔值,是否忽略空值,默认为True,如果设为False,那么有空值的list拼接后返回的是None
方法最终返回str类型
df = pl.DataFrame({"s": [["a", "b", "c"], ["x", "y",None]]})
print(df.with_columns(join=pl.col("s").list.join("@")))
print(df.with_columns(join=pl.col("s").list.join(separator='@',ignore_nulls=False)))
reverse()
df = pl.DataFrame(
{
"a": [[3, 2, 1], [9, 1, 2]],
}
)
df.with_columns(reverse=pl.col("a").list.reverse())
slice()有两个参数:
offset :起始索引
length :截取长度
df = pl.DataFrame({"a": [[1, 2, 3, 4], [10, 2, 1]]})
df.with_columns(slice=pl.col("a").list.slice(1, 2)) slice()只能连续截取还是不如gather()
sort()有两个参数:
descending :接受布尔值,是否降序排序,默认为False升序
nulls_last :接受布尔值,是否把空值放最后,默认为False
df = pl.DataFrame(
{
"a": [[3, 2, 1,None], [9, 1, 2,None]],
}
)
df.with_columns(sort=pl.col("a").list.sort(descending=True)) , df.with_columns(sort=pl.col("a").list.sort(descending=True,nulls_last=True))
7、操作与选择
这一节内容也不多,很好理解
方法 | 描述 |
---|---|
backward_fill() | 向后填充空值 |
cast() | 改变列的数据类型,这个不用讲了吧 |
ceil() | 向上取整 |
cut() | 给连续值分箱 |
drop_nans() | 略 |
drop_nulls() | 略 |
explode() | 跟list.explode()一样 |
fill_nan() | 略 |
fill_null() | 略 |
filter() | 筛选该列符合条件的行 |
flatten() | 把多维list展开成一维 |
floor() | 向下取整 |
forward_fill() | 向前填充 |
head() | 取该列前n行 |
interpolate() | 对空值插值 |
replace() | 替换值 |
reshape() | 改变列的形状 |
reverse() | 反转列 |
round() | 四舍五入到指定位数 |
sample() | 取样 |
shrink_dtype() | 调整数值列的数据类型使之大小恰当 |
shuffle() | 打乱该列所有值的顺序 |
slice() | 获取该列切片 |
sum() | 求和 |
tail() | 获取list最后n个数据 |
由于累的肝疼,我只讲以前没碰见过的
backward_fill()用该列前面的值填充后面的空值,forward_fill()与此相反,backward_fill()有一个参数:
limit : 最多填充几个空值
df = pl.DataFrame(
{
"a": [1, 2, None],
"b": [4, None, 6],
"c": [None, None, 2],
}
)
df.select(pl.all().backward_fill()), df.select(pl.all().backward_fill(limit=1))
cut()用于给列分箱,有四个参数,三个重要:
breaks :接受列表,各个分箱点
labels :接受列表,各个箱的名称,这个参数可以不传
left_closed :接受布尔值,是否将区间设为左闭,默认False右闭
df = pl.DataFrame({"foo": [-2, -1, 0, 1, 2]})
print(df.with_columns(pl.col("foo").cut([-1, 1], labels=["a", "b", "c"]).alias("cut")))
print(df.with_columns(pl.col("foo").cut([-1, 1], labels=["a", "b", "c"],left_closed=True).alias("cut")))
interpolate()参数:
method : {‘linear’, ‘nearest’}
就不讲了
replace()有两个参数:
old :接受单个值或列表,旧值
new : 接受单个值或列表,必须与old一一对应,新值
df = pl.DataFrame({"a": [1, 2, 2, 3]})
print(df.with_columns(replaced=pl.col("a").replace(2, 100)))
print(df.with_columns(replaced=pl.col("a").replace([2, 3], [100, 200])))
reshape()有一个参数:
dimensions :接受元组要转化的形状
df = pl.DataFrame({"foo": [1, 2, 3, 4, 5, 6, 7, 8, 9]})
square = df.select(pl.col("foo").reshape((3, 3)))
print(square)
print(square.select(pl.col("foo").reshape((9,))))
8、Name API
这个API用于对列名的操作,相对来说不太重要,这里只说主要的减少学习负担:
方法 | 描述 |
---|---|
prefix() | 给列名加前缀 |
suffix() | 给列名加后缀 |
to_lowercase() | 转小写 |
to_uppercase() | 转大写 |
prefix()和suffix()相对
df = pl.DataFrame(
{
"a": [1, 2, 3],
"b": ["x", "y", "z"],
}
)
df.with_columns(pl.all().reverse().name.prefix("reverse\_"))
to_lowercase()和to_uppercase()相对:
df = pl.DataFrame(
{
"ColX": [1, 2, 3],
"ColY": ["x", "y", "z"],
}
)
df.with_columns(pl.all().name.to_uppercase())
9、str API
str API的方法是给str类型的列使用的,这节方法多,但是都很简单,只挑典型的将,其他的以此类推就行:
方法 | 描述 |
---|---|
concat() | 把该列所有值拼接起来 |
contains() | 列中的值是否包含指定表达式 |
count_matches() | 返回该列每行用指定表达式匹配到的数量 |
ends_with() | 是否以指定内容结尾 |
extract() | 返回按正则表达式匹配到的数据 |
extract_all() | 以list形式返回按正则表达式匹配到的所有数据 |
head() | 获取前n个字或字母 |
len_chars() | 每个值的长度 |
pad_end() | 末端填充字符串,直到达到某个长度 |
pad_start() | 略 |
replace() | 替换值 |
slice() | 切片 |
split() | 按照某个特定字符串分割 |
starts_with() | 是否以指定内容开头 |
strip_chars() | 去除两端的特定字符 |
strip_chars_start() | 略 |
strip_chars_end() | 略 |
starts_with() | 是否以指定内容开头 |
tail() | 获取前n个字或字母 |
to_date() | str转date |
to_datetime() | str转datetime |
to_lowercase() | 转小写 |
to_time() | str转time |
to_titlecase() | 每个词首字母大写 |
to_uppercase() | 小写转大写 |
concat()有两个参数:
delimiter :分隔符
ignore_nulls :接受布尔值,是否忽略空值,默认为True忽略,若是False那么只要列有空值那么返回就为空
df = pl.DataFrame({"foo": [1, None, 2]})
print(df.select(pl.col("foo").str.concat("-")))
print(df.select(pl.col("foo").str.concat("-", ignore_nulls=False)))
contains()有三个参数,两个重要:
pattern :表达式,可以是正则也可以是字符串
literal :接受布尔值,是否把pattern看成是文本而不是正则表达式,默认是False
import polars as pl
df = pl.DataFrame({"txt": ["Crab", "cat and dog", "rab$bit", None]})
df.select(
pl.col("txt"),
pl.col("txt").str.contains("cat|bit").alias("regex"),
pl.col("txt").str.contains("rab$", literal=True).alias("literal"),
)
count_matches()有两个参数:
pattern :表达式,可以是正则也可以是字符串
literal :接受布尔值,是否把pattern看成是文本而不是正则表达式,默认是False
df = pl.DataFrame({"foo": ["123 bla 45 asd", "xyz 678 910t", "bar", None]})
print(df.with_columns(pl.col("foo").str.count_matches(r"\d").alias("count\_digits")))
df = pl.DataFrame({"bar": ["12 dbc 3xy", "cat\\w", "1zy3\\d\\d", None]})
print(df.with_columns(pl.col("bar").str.count_matches(r"\d", literal=True).alias("count\_digits")))
ends_with() 和 starts_with()是相同的原理:
import polars as pl
df = pl.DataFrame({"fruits": ["apple", "mango", None]})
df.with_columns(
pl.col("fruits").str.ends_with("go").alias("has\_suffix"),
)
extract()有两个参数:
pattern :要匹配的正则表达式
group_index :要取的值的索引号,只能选1,0,选零表示返回整个表达式的匹配,选1返回第一个括号里的匹配
df = pl.DataFrame(
{
"url": [
"http://vote.com/ballon\_dor?error=404&ref=unknown",
"http://vote.com/ballon\_dor?ref=polars&candidate=messi",
"http://vote.com/ballon\_dor?candidate=ronaldo&ref=polars",
]
}
)
df.select(
pl.col("url").str.extract(r"candidate=(\w+)", 0).alias("candidate"),
pl.col("url").str.extract(r"ref=(\w+)", 1).alias("referer"),
pl.col("url").str.extract(r"error=(\w+)", 1).alias("error"),
)
extract_all()有一个参数:
pattern :要匹配的正则表达式
df = pl.DataFrame({"foo": ["123 bla 45 asd", "xyz 678 910t", "bar", None]})
df.select(
pl.col("foo").str.extract_all(r"\d+").alias("extracted\_nrs"),
)
len_chars()
df = pl.DataFrame({"a": ["Café", "345", "東京", None]})
df.with_columns(
pl.col("a").str.len_chars().alias("n\_chars"),
)
pad_start()和pad_end()原理一样,pad_start()有两个参数:
length :要达到的长度
fill_char :以什么填充
df = pl.DataFrame({"a": ["cow", "monkey", "hippopotamus", None]})
df.with_columns(padded=pl.col("a").str.pad_start(8, "*"))
replace()有四个参数:
pattern :要匹配的表达式,接受正则表达式和str
value :以什么值做替换
value :接受布尔值,是否把正则pattern当做str,默认是False
n :接受int,对于该列的每个值,如果有多个匹配,最多应该替换多少次,默认为1
df = pl.DataFrame({"cost": ["12.34", "56.78"]})
df.with_columns(
cost_usd=pl.col("cost").str.replace(r"(\d+)", "$$${1}")
)
slice()有两个参数:
offset :起始索引
length :要取的长度
df = pl.DataFrame({"s": ["pear", None, "papaya", "dragonfruit"]})
df.with_columns(pl.col("s").str.slice(4, length=3).alias("slice"))
split()有两个参数:
by :以什么做分割符
inclusive :是否包含分割符
df = pl.DataFrame({"s": ["foo bar", "foo\_bar", "foo\_bar\_baz",None]})
df.with_columns(
pl.col("s").str.split(by="\_").alias("split"),
pl.col("s").str.split(by="\_", inclusive=True).alias("split\_inclusive"),
)
strip_chars()和strip_chars_start()和strip_chars_end()原理一样
strip_chars()有一个参数:
characters :要移除的内容,默认去除一切符号
df = pl.DataFrame({"foo": [" hello", "\nworld"]})
df, df.with_columns(foo_stripped=pl.col("foo").str.strip_chars())
to_date()、to_datetime()、to_time()原理相同
to_date()有四个参数,三个重要:
format :日期格式
strict :接受布尔值,是否程序有问题就报错,默认是strict不报错
exact :是否需要精确的格式匹配。如果为 False,则允许格式匹配目标字符串中的任何位置,默认为True
日期时间格式的种类:https://docs.rs/chrono/latest/chrono/format/strftime/index.html看这个
日常使用的就是"%Y-%m-%d"
s = pl.Series(["2020/01/01", "2020/02/01", "2020/03/01"])
s.str.to_date()
10、Struct API
关于它,就记个unnest()吧,没必要在增加其他学习成本。
df = pl.DataFrame(
{
"aaa": [1, 2],
"bbb": ["ab", "cd"],
"ccc": [True, None],
"ddd": [[1, 2], [3]],
}
).select(pl.struct("aaa", "bbb", "ccc", "ddd").alias("struct\_col"))
df, df.select(pl.col("struct\_col").struct.unnest())
11、Temporal API
这一节更简单,使用这个API的时候要用dt.function()
方法 | 描述 |
---|---|
century() | 返回当前日期是多少世纪 |
day() | 当前日期是本月第几天 |
month() | 当前日期是本年第几月 |
month_end() | 把日期改成本月末 |
month_start() | 把日期改成本月第一天 |
offset_by() | 让该列日期整体偏移特定时长 |
ordinal_day() | 当前日期是本年第几天 |
quarter() | 返回当前日期是本年第几季度 |
to_string() | 日期转str |
total_days() | 计算时间差里对应几天 |
week() | 返回当前日期是本年第几周 |
weekday() | 返回当前日期是周几 |
century、month,quarter,week,weekdays,day、ordinal_day()原理一样,这里只举一个例子:
from datetime import date
df = pl.DataFrame(
{"date": [date(2001, 1, 1), date(2001, 6, 30), date(2001, 12, 27)]}
)
df.with_columns(pl.col("date").dt.week().alias("week"))
from datetime import datetime
df = pl.DataFrame(
{
"dates": pl.datetime_range(
datetime(2000, 1, 15, 2),
datetime(2000, 12, 15, 2),
"1mo",
eager=True,
)
}
)
df.with_columns(pl.col("dates").dt.month_start().alias('aaa'), pl.col("dates").dt.month_end().alias('bbb'))
offset_by()有一个参数:
by : 偏移多远,可为正为负
1s (1 second)
1m (1 minute)
1h (1 hour)
1d (1 calendar day)
1w (1 calendar week)
1mo (1 calendar month)
1q (1 calendar quarter)
1y (1 calendar year)
from datetime import datetime
df = pl.DataFrame(
{
"dates": pl.datetime_range(
datetime(2000, 1, 1), datetime(2005, 1, 1), "1y", eager=True
),
"offset": ["1d", "2d", "-1d", "1mo", None, "1y"],
}
)
df.select(
[
pl.col("dates").dt.offset_by("1y").alias("date\_plus\_1y"),
pl.col("dates").dt.offset_by("-1y2mo").alias("date\_min"),
]
)
from datetime import timedelta
df = pl.DataFrame(
{
'时间差':[timedelta(days=1000),timedelta(days=809),timedelta(days=123)]
}
)
df.with_columns(pl.col('时间差').dt.total_days())
12、窗口函数
对非日期列用:over()
对日期列用rolling()制造滑动窗口
over()有三个参数,两个重要:
partition_by :按哪个或者哪些列进行分窗,多个值传list
order_by :按哪个列排序,可以用list传多个列
df = pl.DataFrame(
{
"a": ["a", "a", "b", "b", "b"],
"b": [1, 2, 3, 5, 3],
"c": [5, 4, 3, 2, 1],
}
)
df.with_columns(c_max=pl.col("c").max().over("a"))
rolling()有四个参数:
index_column :基于哪个列制造滑动窗口
period :窗口长度
offset :第一个窗口开始的时间,默认是 -period
closed :窗口封闭状态,可选
原理解释:
假设我们有一列日期值(t1,t2,t3,t4),那么若我们只传index_column和period的话我们会得到([t1 - period, t1], [t2 - period, t2], [t3 - period, t3], [t4 - period, t4])的新窗口列,如果加入了offset,那么新的窗口将是([t1 + offset, t1 + offset+period], [t2 + offset, t2 + offset + period] ……)
import polars as pl
dates = [ "2020-01-01 13:45:48", "2020-01-01 16:42:13", "2020-01-01 16:45:09", "2020-01-02 18:12:48", "2020-01-03 19:45:32", "2020-01-08 23:16:43", ]
df = pl.DataFrame({"dt": dates, "a": [3, 7, 5, 9, 2, 1]}).with_columns(
pl.col("dt").str.strptime(pl.Datetime).set_sorted()
)
df.with_columns(
sum_a=pl.sum("a").rolling(index_column="dt", period="2d"),
min_a=pl.min("a").rolling(index_column="dt", period="2d"),
max_a=pl.max("a").rolling(index_column="dt", period="2d"),
)
到这里,本章结束
本章很简单
1、csv
read_csv() 读取CSV
参数很多,我们只挑重要的
source :文件路径
has_header :是否把第一行认为列名,默认为True
schema_overrides :传入一个字典类型,格式为{‘列名’:数据类型……}来提前设置各列数据类型,可以不全
null_values :在读取文件时就把某些值认为成null值。接受list和单个str
try_parse_dates :读取文件时就尝试解析时间类型
scan_csv() 读取为lazyframe
参数跟上面一样
write_csv 把Dataframe写成CSV文件
参数不用记
五、config 模块
这个模块是调整整体配置的,比如让列显示全不再省略,等等
方法 | 描述 |
---|---|
set_fmt_str_lengths() | 设置str列的值最大显示长度 |
set_fmt_table_cell_list_len() | 设置list列的值最大显示长度 |
set_tbl_cell_alignment() | 调成表格的对齐方式 |
set_tbl_cols() | 最大显示多少列不省略 |
set_tbl_rows() | 最大显示多少行不省略 |
使用方式为with pl.config(………………): ………………
set_fmt_str_lengths()
df = pl.DataFrame(
{
"txt": [ "Play it, Sam. Play 'As Time Goes By'.", "This is the beginning of a beautiful friendship.", ]
}
)
df.with_columns(pl.col("txt").str.len_bytes().alias("len"))
with pl.Config(fmt_str_lengths=50):
print(df)
**set_fmt_table_cell_list_len()**
df = pl.DataFrame(
{
"nums": [
[1, 2, 3, 4, 5, 6],
]
}
)
df
with pl.Config(fmt_table_cell_list_len=10): print(df)
set_tbl_cell_alignment() :
可选{LEFT,CENTER,RIGHT}
df = pl.DataFrame(
{"column\_abc": [1.0, 2.5, 5.0], "column\_xyz": [True, False, True]}
)
pl.Config.set_tbl_cell_alignment("RIGHT")
print(df)
set_tbl_cols()
df = pl.DataFrame({str(i): [i] for i in range(100)})
print(df)
with pl.Config(set_tbl_cols=30): print(df)
set_tbl_rows() 同理
结束
到这里、整个polars的知识都遍历完了能一路看下来的全是真心想学好Polars的,前面我说了,我也会拿着个教程当复习笔记,我有责任把这个教程写的尽善尽美,这个教程太详细了,属于是暴力拆解Polars整体结构,能看一遍的话基本使用Polars就没问题了。前路漫漫还有很多知识要学,磨刀不误砍柴工,适时放弃pandas改用Polars会节省大量时间。我们一起努力!