Arrow 具有豐富的資料類型系統,其中包括許多 R 資料類型的直接類比,以及許多 R 中沒有對應項的資料類型。本文描述了 Arrow 類型系統,將其與 R 資料類型進行比較,並概述了資料從 Arrow 傳輸到 R 時使用的預設對應。在文章的末尾,有兩個查閱表:一個描述預設的「R 到 Arrow」類型對應,另一個描述「Arrow 到 R」對應。
動機範例
為了說明需要進行的轉換,請考慮當我們使用 dplyr::glimpse()
來檢查原始格式的 starwars
資料時(作為 R 中的資料框)的輸出,以及當我們首先透過呼叫 arrow_table()
將其轉換為 Arrow Table 時獲得的輸出之間的差異
## Rows: 87
## Columns: 14
## $ name <chr> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia Or~
## $ height <int> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180, 2~
## $ mass <dbl> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, 77.~
## $ hair_color <chr> "blond", NA, NA, "none", "brown", "brown, grey", "brown", N~
## $ skin_color <chr> "fair", "gold", "white, blue", "white", "light", "light", "~
## $ eye_color <chr> "blue", "yellow", "red", "yellow", "brown", "blue", "blue",~
## $ birth_year <dbl> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.0, ~
## $ sex <chr> "male", "none", "none", "male", "female", "male", "female",~
## $ gender <chr> "masculine", "masculine", "masculine", "masculine", "femini~
## $ homeworld <chr> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan", "T~
## $ species <chr> "Human", "Droid", "Droid", "Human", "Human", "Human", "Huma~
## $ films <list> <"A New Hope", "The Empire Strikes Back", "Return of the J~
## $ vehicles <list> <"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "Imp~
## $ starships <list> <"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1",~
glimpse(arrow_table(starwars))
## Table
## 87 rows x 14 columns
## $ name <string> "Luke Skywalker", "C-3PO", "R2-D2", "Darth Vader", "Leia~
## $ height <int32> 172, 167, 96, 202, 150, 178, 165, 97, 183, 182, 188, 180~
## $ mass <double> 77.0, 75.0, 32.0, 136.0, 49.0, 120.0, 75.0, 32.0, 84.0, ~
## $ hair_color <string> "blond", NA, NA, "none", "brown", "brown, grey", "brown"~
## $ skin_color <string> "fair", "gold", "white, blue", "white", "light", "light"~
## $ eye_color <string> "blue", "yellow", "red", "yellow", "brown", "blue", "blu~
## $ birth_year <double> 19.0, 112.0, 33.0, 41.9, 19.0, 52.0, 47.0, NA, 24.0, 57.~
## $ sex <string> "male", "none", "none", "male", "female", "male", "femal~
## $ gender <string> "masculine", "masculine", "masculine", "masculine", "fem~
## $ homeworld <string> "Tatooine", "Tatooine", "Naboo", "Tatooine", "Alderaan",~
## $ species <string> "Human", "Droid", "Droid", "Human", "Human", "Human", "H~
## $ films <list<...>> <"A New Hope", "The Empire Strikes Back", "Return of the~
## $ vehicles <list<...>> <"Snowspeeder", "Imperial Speeder Bike">, <>, <>, <>, "I~
## $ starships <list<...>> <"X-wing", "Imperial shuttle">, <>, <>, "TIE Advanced x1~
## Call `print()` for full schema details
表示的資料本質上是相同的,但欄的資料類型描述已更改。例如
-
name
在資料框中標記為<chr>
(字元向量);在 Arrow Table 中標記為<string>
(字串類型,也稱為 utf8 類型) -
height
在資料框中標記為<int>
(整數向量);在 Arrow Table 中標記為<int32>
(32 位元帶符號整數) -
mass
在資料框中標記為<dbl>
(數值向量);在 Arrow Table 中標記為<double>
(64 位元浮點數)
其中一些差異純粹是表面上的:R 中的整數實際上是 32 位元帶符號整數,因此 Arrow 和 R 中的基礎資料類型彼此直接類比。在其他情況下,差異純粹是關於實作:Arrow 和 R 具有不同的方式來儲存字串向量,但在高層次的抽象中,R 字元類型和 Arrow 字串類型可以被視為直接類比。然而,在某些情況下,沒有明確的類比:雖然 Arrow 具有 POSIXct (時間戳記類型) 的類比,但它沒有 POSIXlt 的類比;相反地,雖然 R 可以表示 32 位元帶符號整數,但它沒有 64 位元無符號整數的等效項。
當 arrow 套件在 R 資料和 Arrow 資料之間轉換時,它會首先檢查是否已提供 Schema – 請參閱 schema()
以取得更多資訊 – 如果沒有可用的 Schema,它將嘗試透過遵循預設對應來猜測適當的類型。本文末尾提供了這些對應的完整列表,但最常見的情況如下圖所示
在此圖片中,黑色方框表示 R 資料類型,淺藍色方框表示 Arrow 資料類型。方向箭頭指定轉換 (例如,邏輯 R 類型和布林值 Arrow 類型之間的雙向箭頭表示邏輯 R 轉換為 Arrow 布林值,反之亦然)。實線表示此轉換規則始終是預設值;虛線表示它僅有時適用 (規則和特殊情況如下所述)。
邏輯/布林值類型
Arrow 和 R 都使用三值邏輯。在 R 中,邏輯值可以是 TRUE
或 FALSE
,NA
用於表示遺失的資料。在 Arrow 中,對應的布林值類型可以取值 true
、false
或 null
,如下所示
chunked_array(c(TRUE, FALSE, NA), type = boolean()) # default
## ChunkedArray
## <bool>
## [
## [
## true,
## false,
## null
## ]
## ]
在此範例中,嚴格來說沒有必要設定 type = boolean()
,因為 arrow 中的預設行為是將 R 邏輯向量轉換為 Arrow 布林值,反之亦然。然而,為了清楚起見,我們將在本文中明確指定資料類型。我們也將使用 chunked_array()
從 R 物件建立 Arrow 資料,並使用 as.vector()
從 Arrow 物件建立 R 資料,但如果我們使用其他方法,也會獲得類似的結果。
整數類型
基礎 R 原生僅支援一種整數類型,使用 32 位元來表示 -2147483648 和 2147483647 之間的帶符號數字,儘管 R 也可以透過 bit64
套件支援 64 位元整數。Arrow 從 C++ 繼承了帶符號和無符號整數類型,版本包括 8 位元、16 位元、32 位元和 64 位元
描述 | 資料類型函數 | 最小值 | 最大值 |
---|---|---|---|
8 位元無符號 | uint8() |
0 | 255 |
16 位元無符號 | uint16() |
0 | 65535 |
32 位元無符號 | uint32() |
0 | 4294967295 |
64 位元無符號 | uint64() |
0 | 18446744073709551615 |
8 位元帶符號 | int8() |
-128 | 127 |
16 位元帶符號 | int16() |
-32768 | 32767 |
32 位元帶符號 | int32() |
-2147483648 | 2147483647 |
64 位元帶符號 | int64() |
-9223372036854775808 | 9223372036854775807 |
預設情況下,arrow 將 R 整數轉換為 Arrow 中的 int32 類型,但您可以透過明確指定另一個整數類型來覆寫此設定
chunked_array(c(10L, 3L, 200L), type = int32()) # default
## ChunkedArray
## <int32>
## [
## [
## 10,
## 3,
## 200
## ]
## ]
chunked_array(c(10L, 3L, 200L), type = int64())
## ChunkedArray
## <int64>
## [
## [
## 10,
## 3,
## 200
## ]
## ]
如果 R 中的值未落在對應 Arrow 類型的允許範圍內,arrow 會擲回錯誤
chunked_array(c(10L, 3L, 200L), type = int8())
## Error: Invalid: value outside of range
從 Arrow 轉換為 R 時,除非以下例外情況之一適用,否則整數類型始終轉換為 R 整數
- 如果 Arrow uint32 或 uint64 的值超出 R 整數允許的範圍,則結果將是 R 中的數值向量
- 如果 Arrow int64 變數的值超出 R 整數允許的範圍,則結果將是 R 中的
bit64::integer64
向量 - 如果使用者設定
options(arrow.int64_downcast = FALSE)
,則 Arrow int64 類型始終產生 R 中的bit64::integer64
向量,而與值無關
浮點數值類型
R 有一種雙精度 (64 位元) 數值類型,預設情況下會轉換為 Arrow 64 位元浮點類型。Arrow 支援單精度 (32 位元) 和雙精度 (64 位元) 浮點數,分別使用 float32()
和 float64()
資料類型函數指定。這兩種類型都轉換為 R 中的雙精度浮點數。範例如下所示
chunked_array(c(0.1, 0.2, 0.3), type = float64()) # default
## ChunkedArray
## <double>
## [
## [
## 0.1,
## 0.2,
## 0.3
## ]
## ]
chunked_array(c(0.1, 0.2, 0.3), type = float32())
## ChunkedArray
## <float>
## [
## [
## 0.1,
## 0.2,
## 0.3
## ]
## ]
arrow_double <- chunked_array(c(0.1, 0.2, 0.3), type = float64())
as.vector(arrow_double)
## [1] 0.1 0.2 0.3
請注意,Arrow 規格也允許半精度 (16 位元) 浮點數,但這些尚未實作。
定點十進制類型
Arrow 也包含 decimal()
資料類型,其中數值以十進制格式而非二進制格式指定。Arrow 中的十進制數分為兩種,128 位元版本和 256 位元版本,但在大多數情況下,使用者應該能夠使用更通用的 decimal()
資料類型函數,而不是特定的 decimal128()
和 decimal256()
函數。
Arrow 中的十進制類型是定精度數字 (而非浮點數),這表示必須明確指定 precision
和 scale
引數
-
precision
指定要儲存的有效位數。 -
scale
指定應儲存在小數點後的位數。如果您設定scale = 2
,則小數點後將準確儲存兩位數字。如果您設定scale = 0
,則值將四捨五入到最接近的整數。也允許負刻度 (在處理極大數字時很方便),因此scale = -2
將值儲存到最接近的 100。
由於 R 沒有任何原生建立十進制類型的方法,因此下面的範例有點迂迴。首先,我們將一些浮點數建立為 Chunked Array,然後在 Arrow 中將這些明確轉換為十進制類型。這是可能的,因為 Chunked Array 物件具有 cast()
方法
arrow_floating <- chunked_array(c(.01, .1, 1, 10, 100))
arrow_decimals <- arrow_floating$cast(decimal(precision = 5, scale = 2))
arrow_decimals
## ChunkedArray
## <decimal128(5, 2)>
## [
## [
## 0.01,
## 0.10,
## 1.00,
## 10.00,
## 100.00
## ]
## ]
雖然十進制類型在 R 中不是原生使用的,但在特別重要的是避免浮點運算中出現問題的情況下,十進制類型可能很有用。
字串/字元類型
R 使用單一字元類型來表示字串,而 Arrow 有兩種類型。在 Arrow C++ 程式庫中,這些類型稱為 strings 和 large_strings,但為了避免 arrow R 套件中的歧義,它們使用 utf8()
和 large_utf8()
資料類型函數定義。這兩種 Arrow 類型之間的區別不太可能對 R 使用者很重要,儘管在關於 資料物件佈局 的文章中討論了差異。
預設行為是將 R 字元向量轉換為 utf8/字串類型,並將兩種 Arrow 類型都轉換為 R 字元向量
strings <- chunked_array(c("oh", "well", "whatever"))
strings
## ChunkedArray
## <string>
## [
## [
## "oh",
## "well",
## "whatever"
## ]
## ]
as.vector(strings)
## [1] "oh" "well" "whatever"
因子/字典類型
Arrow 中 R 因子的類比是字典類型。因子轉換為字典,反之亦然。為了說明這一點,讓我們在 R 中建立一個小的因子物件
## [1] cat dog pig dog
## Levels: cat dog pig
轉換為 Arrow 時,結果是這個字典
dict <- chunked_array(fct, type = dictionary())
dict
## ChunkedArray
## <dictionary<values=string, indices=int32>>
## [
##
## -- dictionary:
## [
## "cat",
## "dog",
## "pig"
## ]
## -- indices:
## [
## 0,
## 1,
## 2,
## 1
## ]
## ]
轉換回 R 時,我們恢復了原始因子
as.vector(dict)
## [1] cat dog pig dog
## Levels: cat dog pig
Arrow 字典比 R 因子稍微靈活一些:字典中的值不一定必須是字串,但因子中的標籤是。因此,當轉換為 R 時,Arrow 字典中的非字串值會被強制轉換為字串。
日期類型
在 R 中,日期通常使用 Date 類別表示。在內部,Date 物件是一種數值類型,其值計算自 Unix 紀元 (1970 年 1 月 1 日) 開始的天數。Arrow 提供兩種可用於表示日期的資料類型:date32 類型和 date64 類型。date32 類型與 R 中的 Date 類別類似:在內部,它儲存一個 32 位元整數,該整數計算自 1970 年 1 月 1 日以來的天數。arrow 中的預設值是將 R Date 物件轉換為 Arrow date32 類型
## [1] "1989-06-15" "1991-09-24" "1993-09-13"
nirvana_32 <- chunked_array(nirvana_album_dates, type = date32()) # default
nirvana_32
## ChunkedArray
## <date32[day]>
## [
## [
## 1989-06-15,
## 1991-09-24,
## 1993-09-13
## ]
## ]
Arrow 也提供更高精度的 date64 類型,其中日期表示為 64 位元整數,該整數編碼自 1970-01-01 00:00 UTC 以來的毫秒數
nirvana_64 <- chunked_array(nirvana_album_dates, type = date64())
nirvana_64
## ChunkedArray
## <date64[ms]>
## [
## [
## 1989-06-15,
## 1991-09-24,
## 1993-09-13
## ]
## ]
從 Arrow 到 R 的轉換有所不同。在內部,date32 類型與 R Date 非常相似,因此這些物件會作為 Date 轉換為 R
## [1] "Date"
但是,由於 date64 類型指定為毫秒級精度,因此它們會作為 POSIXct 時間轉換為 R,以避免遺失相關資訊的可能性
## [1] "POSIXct" "POSIXt"
時間/時間戳記類型
在 R 中,有兩個類別用於表示日期和時間資訊,POSIXct 和 POSIXlt。Arrow 只有一個:時間戳記類型。Arrow 時間戳記與 POSIXct 類別鬆散地類比。在內部,POSIXct 物件將日期表示為數值變數,該變數儲存自 1970-01-01 00:00 UTC 以來的秒數。在內部,Arrow 時間戳記是一個 64 位元整數,計算自 1970-01-01 00:00 UTC 以來的毫秒數。
Arrow 和 R 都支援時區資訊,但在列印的物件中以不同的方式顯示。在 R 中,本地時間會與時區名稱相鄰列印
sydney_newyear <- as.POSIXct("2000-01-01 00:01", tz = "Australia/Sydney")
sydney_newyear
## [1] "2000-01-01 00:01:00 AEDT"
轉換為 Arrow 時,此 POSIXct 物件會變成 Arrow 時間戳記物件。但是,在列印時,時間瞬間始終以 UTC 而非本地時間顯示
sydney_newyear_arrow <- chunked_array(sydney_newyear, type = timestamp())
sydney_newyear_arrow
## ChunkedArray
## <timestamp[s]>
## [
## [
## 1999-12-31 13:01:00
## ]
## ]
但是,時區資訊並未遺失,我們可以透過將 sydney_newyear_arrow
物件轉換回 R POSIXct 物件來輕鬆看到這一點
as.vector(sydney_newyear_arrow)
## [1] "1999-12-31 13:01:00 UTC"
對於 POSIXlt 物件,行為有所不同。在內部,POSIXlt 物件是一個列表,以各種與人類相關的欄位指定「本地時間」。Arrow 中沒有與此類似的類別,因此預設行為是將其轉換為 Arrow 列表。
一天中的時間類型
基礎 R 沒有類別來表示獨立於日期的一天中的時間 (即,無法在不參考特定日期的情況下指定「下午 3 點」),但可以藉助 hms
套件來完成。在內部,hms 物件始終儲存為自 00:00:00 以來的秒數。
Arrow 有兩種用於此目的的資料類型。對於 time32 類型,資料儲存為 32 位元整數,該整數被解釋為自 00:00:00 以來的秒數或毫秒數。請注意以下差異
time_of_day <- hms::hms(56, 34, 12)
chunked_array(time_of_day, type = time32(unit = "s"))
## ChunkedArray
## <time32[s]>
## [
## [
## 12:34:56
## ]
## ]
chunked_array(time_of_day, type = time32(unit = "ms"))
## ChunkedArray
## <time32[ms]>
## [
## [
## 12:34:56.000
## ]
## ]
time64 物件類似,但使用 64 位元整數儲存一天中的時間,並且可以更高的精度表示時間。可以選擇微秒 (unit = "us"
) 或奈秒 (unit = "ns"
),如下所示
chunked_array(time_of_day, type = time64(unit = "us"))
## ChunkedArray
## <time64[us]>
## [
## [
## 12:34:56.000000
## ]
## ]
chunked_array(time_of_day, type = time64(unit = "ns"))
## ChunkedArray
## <time64[ns]>
## [
## [
## 12:34:56.000000000
## ]
## ]
Arrow 中所有版本的 time32 和 time64 物件都會轉換為 R 中的 hms 時間。
持續時間類型
時間長度在 R 中表示為 difftime 物件。Arrow 中類比的資料類型是持續時間類型。持續時間類型儲存為 64 位元整數,可以表示秒數 (預設值,unit = "s"
)、毫秒數 (unit = "ms"
)、微秒數 (unit = "us"
) 或奈秒數 (unit = "ns"
)。為了說明這一點,我們將在 R 中建立一個對應於 278 秒的 difftime
len <- as.difftime(278, unit = "secs")
len
## Time difference of 278 secs
轉換為 Arrow 如下所示
chunked_array(len, type = duration(unit = "s")) # default
## ChunkedArray
## <duration[s]>
## [
## [
## 278
## ]
## ]
chunked_array(len, type = duration(unit = "ns"))
## ChunkedArray
## <duration[ns]>
## [
## [
## 278000000000
## ]
## ]
無論基礎單位為何,Arrow 中的持續時間物件都會轉換為 R 中的 difftime 物件。
預設轉換列表
上面的討論涵蓋了最常見的情況。本節中的兩個表格提供了更完整的列表,說明 arrow 如何在 R 資料類型和 Arrow 資料類型之間進行轉換。在這些表格中,帶有 -
的條目目前尚未實作。
從 R 到 Arrow 的轉換
原始 R 類型 | 轉換後的 Arrow 類型 |
---|---|
logical | boolean |
integer | int32 |
double (“numeric”) | float64 1 |
character | utf8 2 |
factor | dictionary |
raw | uint8 |
Date | date32 |
POSIXct | timestamp |
POSIXlt | struct |
data.frame | struct |
list 3 | list |
bit64::integer64 | int64 |
hms::hms | time32 |
difftime | duration |
vctrs::vctrs_unspecified | null |
1: float64
和 double
在 Arrow C++ 中是相同的概念和資料類型;但是,只有 float64()
在 arrow 中用作函數,因為 double()
已經存在於基礎 R 中
2: 如果字元向量超過 2GB 的字串,它將轉換為 large_utf8
Arrow 類型
3: 只有所有元素都是相同類型的列表才能轉換為 Arrow 列表類型 (它是某種類型的「列表」)。
從 Arrow 到 R 的轉換
原始 Arrow 類型 | 轉換後的 R 類型 |
---|---|
boolean | logical |
int8 | integer |
int16 | integer |
int32 | integer |
int64 | integer 1 |
uint8 | integer |
uint16 | integer |
uint32 | integer 1 |
uint64 | integer 1 |
float16 | - 2 |
float32 | double |
float64 | double |
utf8 | character |
large_utf8 | character |
binary | arrow_binary 3 |
large_binary | arrow_large_binary 3 |
fixed_size_binary | arrow_fixed_size_binary 3 |
date32 | Date |
date64 | POSIXct |
time32 | hms::hms |
time64 | hms::hms |
timestamp | POSIXct |
duration | difftime |
decimal | double |
dictionary | factor 4 |
list | arrow_list 5 |
large_list | arrow_large_list 5 |
fixed_size_list | arrow_fixed_size_list 5 |
struct | data.frame |
null | vctrs::vctrs_unspecified |
map | arrow_list 5 |
union | - 2 |
1: 這些整數類型可能包含超出 R 的 integer
類型 (32 位元帶符號整數) 範圍的值。當它們超出範圍時,uint32
和 uint64
會轉換為 double
(“numeric”),而 int64
會轉換為 bit64::integer64
。可以停用此轉換 (以便 int64
始終產生 bit64::integer64
向量),方法是設定 options(arrow.int64_downcast = FALSE)
。
2: 某些 Arrow 資料類型目前沒有 R 等效項,如果轉換為或透過 schema 對應到 R,則會引發錯誤。
3: arrow*_binary
類別實作為原始向量的列表。
4: 由於 R 因子的限制,Arrow dictionary
值在轉換為 R 時,如果它們還不是字串,則會強制轉換為字串。
5: arrow*_list
類別實作為 vctrs_list_of
的子類別,其中 ptype
屬性設定為值類型的空 Array 轉換成的類型。