如何把SQL查询转换为pandas中的数据查询

在python做数据分析,都会用到与SQL查询语句一样的操作,SQL语句是针对数据库中的数据表,而python中查询数据一般针对pandas的dataframe数据格式。本文以几个比较复杂的SQL查询语句为例,写出对应的pandas查询数据的命令。

如何把SQL查询转换为pandas中的数据查询

数据为“王斌”和“李丽”两个人最近两年出差的飞行记录。

import pandas as pd

df = pd.DataFrame({'name': ['王斌', '王斌', '王斌', '李丽', '李丽'], 
                   'destination': ['北京', '青岛', '兰州','深圳', '杭州'], 
                   'dep_date': ['2021-02-02', '2021-01-01','2022-01-11', '2021-05-05','2022-01-11'], 
                   'duration': [7, 21, 14, 10, 14]})
df

结果如下:

   name	destination	dep_date	duration
0	王斌	北京	2021-02-02	7
1	王斌	青岛	2021-01-01	21
2	王斌	兰州	2022-01-11	14
3	李丽	深圳	2021-05-05	10
4	李丽	杭州	2022-01-11	14

SQL查询语句1:

针对每个人的每次出行,查询其下一次出行时间(lead1)、下下次出行的时间(lead2),其上一次出行的时间(lag1)以及倒数第三次出行的时间(lag3)。

-- SQL查询语句1:
SELECT name
       , destination
       , dep_date
       , duration
       , LEAD(dep_date) OVER(ORDER BY dep_date, name) AS lead1
       , LEAD(dep_date, 2) OVER(ORDER BY dep_date, name) AS lead2
       , LAG(dep_date) OVER(ORDER BY dep_date, name) AS lag1
       , LAG(dep_date, 3) OVER(ORDER BY dep_date, name) AS lag3
FROM df

为了在pandas中获得同样的结果,使用shift()函数。

shift函数:LEAD() and LAG()

在pandas中,编写与SQL查询语句1相对应的命令。因为数据操作的逻辑相同,所以先对数据进行排序,这样做更效率更高,避免了多次对数据进行排序,代码如下:

df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(lead1 = df['dep_date'].shift(-1),
          lead2 = df['dep_date'].shift(-2),
          lag1 = df['dep_date'].shift(),
          lag3 = df['dep_date'].shift(3))

查询数据结果如下图:

如何把SQL查询转换为pandas中的数据查询

如果写成以下形式,查询效率就要差一些:

df.assign(lead1 = df.sort_values(['dep_date', 'name'])['dep_date'].shift(-1),
          lead2 = df.sort_values(['dep_date', 'name'])['dep_date'].shift(-2),
          lag1 = df.sort_values(['dep_date', 'name'])['dep_date'].shift(),
          lag3 = df.sort_values(['dep_date', 'name'])['dep_date'].shift(3))

结果如下图,其输出结果与前面的写法相同。

如何把SQL查询转换为pandas中的数据查询

Date/datetime函数: DATENAME(), DATEDIFF(), DATEADD()

SQL查询语句2:

SELECT name
       , destination
       , dep_date
       , duration
       , DATENAME(WEEKDAY, dep_date) AS day
       , DATENAME(MONTH, dep_date) AS month
       , DATEDIFF(DAY,  
                  LAG(dep_date) OVER(ORDER BY dep_date, name), 
                  dep_date) AS diff
       , DATEADD(DAY, day, dep_date) AS arr_date
FROM df

这一查询要得到出行日期所属的星期名称(Sunday、Monday…..)、月份名称(month)、距离前一次飞行有多少天(diff)以及该次出行到达的日期(arr_date)。

先把出行数据中的列数据类型转换为适当的类型:

df['dep_date'] = pd.to_datetime(df['dep_date'])
df['duration'] = pd.to_timedelta(df['duration'], 'D')

把dep_date 转换为 datetime 类型, 以便能够用 .dt 方法获得日期的年月日数据,如 df[‘dep_date’].dt.year 可以获得年份数据,这与SQL中的 DATEPART(YEAR, dep_date) 方法相同。

pandas中实现与SQL查询语句2结果相同的代码如下:

df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(day = df['dep_date'].dt.day_name(),
          month = df['dep_date'].dt.month_name(),
          diff = df['dep_date'] - df['dep_date'].shift(),
          arr_date = df['dep_date'] + df['duration'])

输出结果如下图:

如何把SQL查询转换为pandas中的数据查询

用 dtypes 属性查看一下 dataframe 中的列的类型,代码如下:

df.sort_values(['dep_date', 'name'], inplace=True)
df.assign(day = df['dep_date'].dt.day_name(),
          month = df['dep_date'].dt.month_name(),
          diff = df['dep_date'] - df['dep_date'].shift(),
          arr_date = df['dep_date'] + df['duration']).dtypes

结果如下图所示:

如何把SQL查询转换为pandas中的数据查询

可以看出,两个日期之间的差、飞行时长的数据类型为 timedelta 类型。

排名函数(Ranking): ROW_NUMBER(), RANK(), DENSE_RANK()

在SQL语言中也有类似 ROW_NUMBER(), RANK(), DENSE_RANK() 的函数,其作用也相同,都是对某列数据做先后排名。基于 duration 数据,分别创建对应的列,能方便地使用这三个函数。使用 ROW_NUMBER() 函数 是 dataframe 中数据行的内置编号,这与其他两个函数不一样。

SQL查询语句3:

SELECT name
       , destination
       , dep_date
       , duration
       , ROW_NUMBER() OVER(ORDER BY duration, name) AS row_number_d
       , RANK() OVER(ORDER BY duration) AS rank_d
       , DENSE_RANK() OVER(ORDER BY duration) AS dense_rank_d
FROM df

与上述SQL查询语句,可以在pandas 中使用 rank() 函数写成以下代码段:

df.sort_values(['duration', 'name']).assign(
    row_number_d = df['duration'].rank(method='first').astype(int),
    rank_d = df['duration'].rank(method='min').astype(int),
    dense_rank_d = df['duration'].rank(method='dense').astype(int))

输出结果如下图:

如何把SQL查询转换为pandas中的数据查询

与前面两部分不一样的是,没有对数据进行排序;使用astype(int) 把浮点数表示的排名值转换为整数。

聚合窗口函数和数据切分(Aggregate window functions 和 partitioning)

SQL查询语句4:

SELECT name
       , destination
       , dep_date 
       , duration
       , MAX(duration) OVER() AS max_dur
       , SUM(duration) OVER() AS sum_dur
       , AVG(duration) OVER(PARTITION BY name) AS avg_dur_name
       , SUM(duration) OVER(PARTITION BY name ORDER BY dep_date
                            RANGE BETWEEN UNBOUNDED PRECEDING
                            AND CURRENT ROW) AS cum_sum_dur_name
FROM df

we will create column for the longest trip duration: max_dur, total duration for all trips: sum_dur, average duration of trip per person: avg_dur_name and cumulative sum of trip length for each person: cum_sum_dur_name.

找出飞行时间最长的出行(max_dur)、所有出行的飞行时间合计(sum_dur)、每个人出行的飞行时长均值(avg_dur_name)以及每个人飞行时长的累计时长(cum_sum_dur_name),并创建相应的列。

与之对应的pandas代码如下:

df.assign(max_dur=df['duration'].max(),
          sum_dur=df['duration'].sum(),
          avg_dur_name=df.groupby('name')['duration']
                         .transform('mean'),
          cum_sum_dur_name=df.sort_values('dep_date')
                             .groupby('name')['duration']
                             .transform('cumsum'))

输出数据的结果如下图:

如何把SQL查询转换为pandas中的数据查询

以上数据是按 dep_date 排序的。因为pandas的广播属性,添加 max_dur 和 sum_dur 这样的聚合统计数据是很简单的。一般来说,如果在pandas中把一个标量值添加到新的一列,该值对所有行都是有效的;把groupby()transform() 结合起来就可实现 PARTITION BY 的功能。

创建了 cum_sum_dur_name 列,可按该数据排序,相应的代码修改如下:

df.sort_values(['name', 'dep_date'], inplace=True)
df.assign(max_dur=df['duration'].max(),
          sum_dur=df['duration'].sum(),
          avg_dur_name=df.groupby('name')['duration']
                         .transform('mean'),
          cum_sum_dur_name=df.groupby('name')['duration']
                             .transform('cumsum'))

输出结果如下图所示:

如何把SQL查询转换为pandas中的数据查询

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/76369
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 2024 年 7 月 11 日
下一篇 2024 年 7 月 11 日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注