nvidia-rapids︱cuDF與pandas一樣的DataFrame庫

cuDF(https://github.com/rapidsai/cudf)是一個基於Python的GPU DataFrame庫,用於處理數據,包括加載、連接、聚合和過濾數據。向GPU的轉移允許大規模的加速,因爲GPU比CPU擁有更多的內核。

筆者覺得,對於我來說一個比較好的使用場景是,代替並行,在pandas處理比較慢的時候,切換到cuDF,就不用寫繁瑣的並行了。


官方文檔:
1 Docs » API Reference
2 rapidsai/cudf

相關參考:

nvidia-rapids︱cuDF與pandas一樣的DataFrame庫
NVIDIA的python-GPU算法生態 ︱ RAPIDS 0.10
nvidia-rapids︱cuML機器學習加速庫
nvidia-rapids︱cuGraph(NetworkX-like)關係圖模型



1 cuDF背景與安裝

1.1 背景

cuDF在過去一年中的發展速度非常之快。每個版本都加入了令人興奮的新功能、優化和錯誤修復。0.10版本也不例外。cuDF 0.10版本的一些新功能包括 groupby.quantile()、Series.isin()、從遠程/雲文件系統(例如hdfs、gcs、s3)讀取、Series和DataFrame isna()、按分組功能中的任意長度Series分組 、Series 協方差和Pearson相關性以及從DataFrame / Series .values 屬性返回 CuPy數組。此外,apply UDF函數API經過了優化,並且加入了通過.iloc訪問器的收集和散播方法。

除了提供所有上述出色的功能、優化和錯誤修復之外,cuDF 0.10版本還花費大量的精力構建未來。該版本將cuStrings存儲庫合併到cuDF中,併爲合併兩個代碼庫做好了準備,使字符串功能能夠被更緊密地集成到cuDF中,以此提供更快的加速和更多的功能。此外,RAPIDS添加了cuStreamz元數據包,因此可以使用cuDF和Streamz庫簡化GPU加速流處理。cuDF繼續改進其Pandas API兼容性和Dask DataFrame互操作性,使我們的用戶可以最大程度地無縫使用cuDF。

在幕後,libcudf的內部架構正在經歷一次重大的重新設計。0.10版本加入了最新的cudf :: column和cudf :: table類,這些類大大提高了內存所有權控制的強健性,併爲將來支持可變大小數據類型(包括字符串列、數組和結構)奠定了基礎。由於已構建對整個libcudf API中的新類的支持,這項工作將在下一個版本週期中繼續進行。此外,libcudf 0.10添加了許多新的API和算法,包括基於排序、支持空數據的分組功能、分組功能分位數和中位數、cudf :: unique_count,cudf :: repeat、cudf :: scatter_to_tables等。與以往一樣,此版本還包括許多其他改進和修復。

RAPIDS內存管理器庫RMM也正在進行一系列重組。這次重組包括一個基於內存資源的新架構,該架構與C ++ 17 std :: pmr :: memory_resource大多兼容。這使該庫更容易在公共接口之後添加新類型的內存分配器。0.10還用Cython取代了CFFI Python綁定,從而使C ++異常可以傳播到Python異常,使更多可調整的錯誤被傳遞給應用程序。下一個版本將繼續提高RMM中的異常支持。

最後,你會注意到cuDF在這個版本中速度有了顯著提升,包括join(最多11倍)、gather和scatter on tables(速度也快2-3倍)的大幅性能改進,以及更多如圖5所示的內容。
在這裏插入圖片描述
圖5:單個NVIDIA Tesla V100(立即免費試用) GPU與雙路Intel Xeon E5–2698 v4 CPU(20核)上的cuDF vs Pandas加速

1.2 安裝

有conda可以直接安裝,也可以使用docker,參考:https://github.com/rapidsai/cudf

conda版本,cudf version == 0.10

# for CUDA 9.2
conda install -c rapidsai -c nvidia -c numba -c conda-forge \
    cudf=0.10 python=3.6 cudatoolkit=9.2

# or, for CUDA 10.0
conda install -c rapidsai -c nvidia -c numba -c conda-forge \
    cudf=0.10 python=3.6 cudatoolkit=10.0

# or, for CUDA 10.1
conda install -c rapidsai -c nvidia -c numba -c conda-forge \
    cudf=0.10 python=3.6 cudatoolkit=10.1

docker版本,可參考:https://rapids.ai/start.html#prerequisites

在這裏插入圖片描述

docker pull rapidsai/rapidsai:cuda10.1-runtime-ubuntu16.04-py3.7
docker run --gpus all --rm -it -p 8888:8888 -p 8787:8787 -p 8786:8786 \
    rapidsai/rapidsai:cuda10.1-runtime-ubuntu16.04-py3.7

2 一些demo

2.1 新建dataframe

import cudf
import numpy as np
from datetime import datetime, timedelta

t0 = datetime.strptime('2018-10-07 12:00:00', '%Y-%m-%d %H:%M:%S')
n = 5
df = cudf.DataFrame({
  'id': np.arange(n),
  'datetimes': np.array([(t0+ timedelta(seconds=x)) for x in range(n)])
})
df

在這裏插入圖片描述

Build DataFrame via list of rows as tuples:

>>> import cudf
>>> df = cudf.DataFrame([
    (5, "cats", "jump", np.nan),
    (2, "dogs", "dig", 7.5),
    (3, "cows", "moo", -2.1, "occasionally"),
])
>>> df
0     1     2     3             4
0  5  cats  jump  null          None
1  2  dogs   dig   7.5          None
2  3  cows   moo  -2.1  occasionally

2.2 pandas 與 cuDF切換

pandas到 cuDF

>>> import pandas as pd
>>> import cudf
>>> pdf = pd.DataFrame({'a': [0, 1, 2, 3],'b': [0.1, 0.2, None, 0.3]})
>>> df = cudf.from_pandas(pdf)
>>> df
  a b
0 0 0.1
1 1 0.2
2 2 nan
3 3 0.3

cuDF 到pandas

>>> import cudf
>>> gdf = cudf.DataFrame({'a': [1, 2, None], 'b': [3, None, 5]})
>>> gdf.fillna(4).to_pandas()
a  b
0  1  3
1  2  4
2  4  5
>>> gdf.fillna({'a': 3, 'b': 4}).to_pandas()
a  b
0  1  3
1  2  4
2  3  5

2.3 選中某行列

df = cudf.DataFrame({'a': list(range(20)),
 'b': list(range(20)),
 'c': list(range(20))})
df

在這裏插入圖片描述

df.iloc[1]

a    1
b    1
c    1
Name: 1, dtype: int64

2.4 apply_rows和apply_chunks

apply_rows

import cudf
import numpy as np
from numba import cuda
 
df = cudf.DataFrame()
df['in1'] = np.arange(1000, dtype=np.float64)
 
def kernel(in1, out):
    for i, x in enumerate(in1):
        print('tid:', cuda.threadIdx.x, 'bid:', cuda.blockIdx.x,
              'array size:', in1.size, 'block threads:', cuda.blockDim.x)
        out[i] = x * 2.0
 
outdf = df.apply_rows(kernel,
                      incols=['in1'],
                      outcols=dict(out=np.float64),
                      kwargs=dict())
 
print(outdf['in1'].sum()*2.0)
print(outdf['out'].sum())

>>> 999000.0
>>> 999000.0

apply_chunks

import cudf
import numpy as np
from numba import cuda
 
 
df = cudf.DataFrame()
df['in1'] = np.arange(100, dtype=np.float64)
 
 
def kernel(in1, out):
    print('tid:', cuda.threadIdx.x, 'bid:', cuda.blockIdx.x,
          'array size:', in1.size, 'block threads:', cuda.blockDim.x)
    for i in range(cuda.threadIdx.x, in1.size, cuda.blockDim.x):
        out[i] = in1[i] * 2.0
 
outdf = df.apply_chunks(kernel,
                        incols=['in1'],
                        outcols=dict(out=np.float64),
                        kwargs=dict(),
                        chunks=16,
                        tpb=8)
 
print(outdf['in1'].sum()*2.0)
print(outdf['out'].sum())


>>> 9900.0
>>> 9900.0

2.5 groupby

from cudf import DataFrame
df = DataFrame()
df['key'] = [0, 0, 1, 1, 2, 2, 2]
df['val'] = [0, 1, 2, 3, 4, 5, 6]
groups = df.groupby(['key'], method='cudf')

# Define a function to apply to each row in a group
def mult(df):
  df['out'] = df['key'] * df['val']
  return df

result = groups.apply(mult)
print(result)

輸出:

   key  val  out
0    0    0    0
1    0    1    0
2    1    2    2
3    1    3    3
4    2    4    8
5    2    5   10
6    2    6   12

之後,用到的時候再追加。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章