DoubleCloud 即将停止服务。利用限时免费迁移服务迁移到 ClickHouse。立即联系我们 ->->

博客 / 工程

使用 ClickHouse 查询 Pandas DataFrame

author avatar
Mark Needham
2023年11月15日

在数据分析领域,Pandas 被认为是大多数基于 Python 的数据探索的起点。但如果我们想使用像 ClickHouse 这样的 OLAP 数据库来查询我们的 DataFrame,以利用其查询引擎和 SQL 支持呢?

这就是由 ClickHouse 提供支持的 Python 库 chDB 发挥作用的地方。我们已经在博客上 几次 介绍过 chDB,但在本文中,我们将重点关注其查询 Pandas DataFrame、将它们连接在一起、聚合数据,然后将结果导出回 Pandas 的能力。

chDB 可通过 PyPi 获得,这意味着我们可以使用 pip 安装它。

pip install chdb

我们还需要安装 Pandas 和 PyArrow,因为 chDB 的 DataFrame 功能依赖于这些库。

pip install pandas pyarrow

好的,现在我们可以开始了。我们将探索 Kaggle 的加拿大主要城市房价数据集,其中包含 2021 年人口普查的房地产数据。

下载 CSV 文件后,我们将将其读取到 Pandas DataFrame 中。

import pandas as pd
house_prices = pd.read_csv(
  filepath_or_buffer="data/HouseListings-Top45Cities-10292023-kaggle.csv", 
  encoding = "ISO-8859-1"
)

然后我们可以查看一些记录。

house_prices.head(n=2).T
                                          0                      1
City                                Toronto                Toronto
Price                              779900.0               799999.0
Address               #318 -20 SOUTHPORT ST  #818 -60 SOUTHPORT ST
Number_Beds                               3                      3
Number_Baths                              2                      1
Province                            Ontario                Ontario
Population                          5647656                5647656
Latitude                            43.7417                43.7417
Longitude                          -79.3733               -79.3733
Median_Family_Income                97000.0                97000.0

使用 ClickHouse 查询 DataFrame

要在 chDB 中查询 DataFrame,我们需要导入 chdb.dataframe 模块。

import chdb.dataframe as cdf

此模块有一个名为 query 的函数,我们可以使用它。我们可以将 1 个或多个 DataFrame 作为命名参数传递,然后可以在查询中引用它们。我们使用的参数名称可以作为 __<parameter-name>__ 引用。以下查询查找房产数量最多的前 10 个城市。

cdf.query(
    house_prices=house_prices, 
    sql="""
FROM __house_prices__
SELECT City, Province, count(*)
GROUP BY ALL
LIMIT 10
""")
             City            Province  count()
    b'White Rock' b'British Columbia'     1175
       b'Toronto'          b'Ontario'     1276
       b'Kelowna' b'British Columbia'     1280
      b'Winnipeg'         b'Manitoba'      530
      b'Winnipeg'          b'Ontario'        1
      b'Red Deer'          b'Alberta'      326
   b'Thunder Bay'          b'Ontario'      154
    b'Lethbridge'          b'Alberta'      379
b'St. Catharines'          b'Ontario'     1268
b'Trois-Rivieres'           b'Quebec'      165

使用 ClickHouse 连接 DataFrame

除了查询单个 DataFrame 外,我们还可以将它们连接在一起。因此,我们将引入另一个包含 加拿大城市元数据 的数据集。让我们看看它。

cities = pd.read_csv(
    filepath_or_buffer="data/canadacities.csv"
)

cities.head(n=1).T
                                                               0
city                                                     Toronto
city_ascii                                               Toronto
province_id                                                   ON
province_name                                            Ontario
lat                                                      43.7417
lng                                                     -79.3733
population                                             5647656.0
density                                                   4427.8
timezone                                         America/Toronto
ranking                                                        1
postal         M5T M5V M5P M5S M5R M5E M5G M5A M5C M5B M5M M5...
id                                                    1124279679

我们可以通过 city_ascii 和 province_name 字段将此 DataFrame 与第一个 DataFrame 连接起来。

top_cities = cdf.query(
    house_prices=house_prices, 
    cities=cities,
    sql="""
FROM __house_prices__ AS hp
JOIN __cities__ AS c 
ON c.city_ascii = hp.City AND c.province_name = hp.Province
SELECT City, Province, count(*),
       round(avg(Price)) AS avgPrice,
       round(max(Price)) AS maxPrice,
       ranking, density
GROUP BY ALL
LIMIT 10
""")

如果查看 top_cities 变量,我们将看到类似以下内容。

           City   Province  count()  avgPrice   maxPrice  ranking  density
   b'Brantford' b'Ontario'      628  955923.0  6495000.0        2   1061.2
    b'Hamilton' b'Ontario'     1289  975543.0 10995000.0        2    509.1
 b'Thunder Bay' b'Ontario'      154  459703.0  5599000.0        2    332.1
     b'Caledon' b'Ontario'     1336 1383366.0  9995000.0        3    111.2
     b'Calgary' b'Alberta'     1322  660046.0  5250000.0        1   1592.4
     b'Windsor' b'Ontario'      720  643019.0  2750000.0        2   1572.8
b'Medicine Hat' b'Alberta'      277  448137.0  1475000.0        3    565.1
    b'Montreal'  b'Quebec'      212  931392.0  4400000.0        1   4833.5
    b'Edmonton' b'Alberta'     1351  425582.0  4463445.0        1   1320.4
     b'Sudbury' b'Ontario'      203  596087.0  7699900.0        2     52.1

top_cities 的类型为 <class 'chdb.dataframe.query.Table'>,我们实际上也可以使用 SQL 查询 chDB 表。我们可以使用 query 函数执行此操作,其中底层表可作为 __table__ 访问。

因此,如果我们想获取 top_cities 的前 5 行,我们可以编写以下内容。

top_cities.query("""
FROM __table__ 
SELECT City, maxPrice, ranking, density 
LIMIT 5
""")
          City   maxPrice  ranking  density
  b'Brantford'  6495000.0        2   1061.2
   b'Hamilton' 10995000.0        2    509.1
b'Thunder Bay'  5599000.0        2    332.1
    b'Caledon'  9995000.0        3    111.2
    b'Calgary'  5250000.0        1   1592.4

将 chDB 表导出到 Pandas DataFrame

如果我们使用 ClickHouse 进行足够的查询,我们始终可以使用 to_pandas 函数将该表转换回 Pandas DataFrame。

top_cities_df = top_cities.to_pandas()

最后,让我们在 Pandas 中进行一些查询。

(top_cities_df[top_cities_df["Province"] == b"Ontario"]
  .sort_values(["ranking", "density"])
  .drop(["Province"], axis=1)
)  
          City  count()  avgPrice   maxPrice  ranking  density
    b'Sudbury'      203  596087.0  7699900.0        2     52.1
b'Thunder Bay'      154  459703.0  5599000.0        2    332.1
   b'Hamilton'     1289  975543.0 10995000.0        2    509.1
  b'Brantford'      628  955923.0  6495000.0        2   1061.2
    b'Windsor'      720  643019.0  2750000.0        2   1572.8
    b'Caledon'     1336 1383366.0  9995000.0        3    111.2

总结

chDB 正在不断发展,但它已经可以做的事情非常酷。所以,请访问 GitHub 页面 并试一试!

分享此文章

订阅我们的时事通讯

随时了解功能发布、产品路线图、支持和云产品信息!
正在加载表单...
关注我们
Twitter imageSlack imageGitHub image
Telegram imageMeetup imageRss image