Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) by romainfrancois · Pull Request #5451 · apache/arrow · GitHub
Skip to content

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific) - #5451

Closed
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression
Closed

ARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)#5451
romainfrancois wants to merge 31 commits into
apache:masterfrom
romainfrancois:ARROW-6532/write_parquet_compression

Conversation

@romainfrancois

@romainfrancoisromainfrancois commented Sep 20, 2019

Copy link
Copy Markdown
Contributor

This adds parameters to write_parquet() to control compression, whether to use dictionary, etc ... on top of the C++ classes parquet::WriterProperties and parquet::ArrowWriterProperties e.g.

write_parquet(tab, file, compression="gzip", compression_level=7)

@nealrichardson

Copy link
Copy Markdown
Member

Yeah, I'm not sure about this on a few levels. I think the R I want to type to write a compressed Parquet file looks like write_parquet(df, file="file.parquet", compression="snappy"). This should be more naturally exposed to the causal user, without having to create a CompressedOutputStream directly.

I'm also not sure whether this works as intended. The Parquet C++ code seems to have its own compression and writing logic; that may be historical artifact, or it may be meaningful. Maybe we can get away without implementing bindings for those classes--the proof would be a passing test of writing a compressed parquet file and reading it back in. Then again, maybe in principle we should write the Parquet bindings to match the C++ library.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

It is not a good idea to write a Parquet file into a CompressedOutputStream. Such file will not be readable with read_parquet.

Parquet already compresses data internally.

@wesm

wesm commented Sep 20, 2019

Copy link
Copy Markdown
Member

Here's the way we handle it in Python, you'll need to do the same thing in R

https://github.com/apache/arrow/blob/master/python/pyarrow/parquet.py#L363

@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from d85b6fc to aa2833eCompareSeptember 24, 2019 11:26
@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

Some progress inspired from the python implementation. write_parquet() gains many parameters:

write_parquet<-function(
table,
sink, chunk_size=NULL,
version=NULL, compression=NULL, use_dictionary=NULL, write_statistics=NULL, data_page_size=NULL,
properties=ParquetWriterProperties$create(
version=version,
compression=compression,
use_dictionary=use_dictionary,
write_statistics=write_statistics,
data_page_size=data_page_size
),
use_deprecated_int96_timestamps=FALSE, coerce_timestamps=NULL, allow_truncated_timestamps=FALSE,
arrow_properties=ParquetArrowWriterProperties$create(
use_deprecated_int96_timestamps=use_deprecated_int96_timestamps,
coerce_timestamps=coerce_timestamps,
allow_truncated_timestamps=allow_truncated_timestamps
)
)

that are managed by the classes ParquetWriterProperties and ParquetArrowWriterProperties.

Only simple versions so far, e.g. compression may only be a single string, so we may do:

library(arrow, warn.conflicts=FALSE)
df<-tibble::tibble(x=1:5)
write_parquet(df, "/tmp/test.parquet", compression="snappy")
read_parquet("/tmp/test.parquet")
#> # A tibble: 5 x 1#> x#> <int>#> 1 1#> 2 2#> 3 3#> 4 4#> 5 5

but we can't e.g. specify specific variables to handle by such and such compression. This is a good place I think for a tidy select, e.g. something like that:

df<-tibble::tibble(x1=1:5, x2=1:5, y=1:5)
write_parquet(df, "/tmp/test.parquet", compression=list(snappy= starts_with("x"))
)

The list in python goes the other way, so if we do something similar it would look like

write_parquet(df, "/tmp/test.parquet", compression=list(x1="snappy", x2="snappy")
)

perhaps we can have compression = only handle the same type of thing python does, but then come up with some helper function so that we'd have e.g.

write_parquet(df, "/tmp/test.parquet", compression= compression_spec(snappy= starts_with("x"))
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

One option we discussed with @nealrichardson was to be able to do e.g.

write_parquet(df, "/tmp/test.parquet", compression=Codec$create("snappy", 5L)
)

But unfortunately, the C++ class arrow::util::Codec does not give a way to swim back to the compression level, so I can't do e.g. compression$level.

Instead, I followed python's lead and we can do this instead:

write_parquet(df, "/tmp/test.parquet", compression="snappy", compression_level=5L
)

@romainfrancois

Copy link
Copy Markdown
ContributorAuthor

These arguments that are handled by ParquetWriterProperties can now be single values, unnamed vectors of the same length as the number of columns in the table, or named vectors: compression, compression_level, use_dictionary and write_statistics.

@nealrichardson

Copy link
Copy Markdown
Member

Taking a look now; FTR Travis says

Missing link or links in documentation object 'write_parquet.Rd':
‘to_arrow’

Comment threadr/R/compression.R Outdated
Comment threadr/R/compression.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/tests/testthat/test-parquet.R Outdated
@codecov-io

codecov-io commented Sep 26, 2019

Copy link
Copy Markdown

Codecov Report

Merging #5451 into master will decrease coverage by 11.93%.
The diff coverage is 65.9%.

Impacted file tree graph

@@ Coverage Diff @@## master #5451 +/- ##
===========================================
- Coverage 88.7% 76.76% -11.94% 
===========================================
Files 964 59 -905 Lines 128215 4330 -123885 Branches 1501 0 -1501 ===========================================
- Hits 113731 3324 -110407 + Misses 14119 1006 -13113 + Partials 365 0 -365
Impacted FilesCoverage Δ
r/R/record-batch.R97.36% <ø> (-0.04%)⬇️
r/R/field.R92.85% <ø> (-0.48%)⬇️
r/src/arrow_types.h96% <ø> (ø)⬆️
r/R/schema.R31.25% <ø> (+7.72%)⬆️
r/R/type.R83.9% <ø> (-0.19%)⬇️
r/R/enums.R0% <ø> (ø)⬆️
r/R/message.R75% <ø> (+21.15%)⬆️
r/R/array.R77.14% <ø> (+4.92%)⬆️
r/src/compression.cpp85.71% <0%> (-14.29%)⬇️
r/R/feather.R63.33% <100%> (ø)⬆️
... and 928 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 46a14db...413dd41. Read the comment docs.

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few more notes. I'd also like to see better coverage on https://codecov.io/gh/apache/arrow/pull/5451/diff

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd write this function as

make_valid_version <- function(version, valid_versions = valid_parquet_version) {
pq_version <- valid_version[[version]]
if (is.null(pq_version)) {
stop('"version" should be one of ', oxford_paste(names(valid_versions), "or"), call.=FALSE)
}
pq_version
}

As it stands, make_valid_version(1) won't work, and it seems like it should.

Per the codecov report, this code isn't being exercised.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wfm:

arrow:::make_valid_version("1.0")
#> [1] 0arrow:::make_valid_version("2.0")
#> [1] 1arrow:::make_valid_version(1)
#> [1] 0arrow:::make_valid_version(2)
#> [1] 1

Created on 2019-09-27 by the reprex package (v0.3.0.9000)

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure there's value including properties and arrow_properties in the signature here. I kept them in read_delim_arrow() because there were some properties they expose that aren't mapped to arguments the readr::read_delim signature but that doesn't seem to be the case here. (On reflection, that's probably not the right call there either; if you want lower-level access to those settings, you should probably be doing CsvTableReader$create(...) anyway.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My rationale was that perhaps you'd already have built those objects properties and arrow_properties before.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I get the point that maybe this could be diverted to using a ParquetFileWriter instance

Comment threadr/tests/testthat/test-parquet.R Outdated
Comment threadr/tests/testthat/helper-parquet.R Outdated

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some notes on the docs

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And not all columns need to be specified, correct?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this follow the same conventions as compression? Maybe there should be a paragraph/section in the docs that explains how these parameters work since it's the same/similar.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored the documentation in @details

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, and what are statistics?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know

Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
Comment threadr/R/parquet.R Outdated
@romainfrancois
romainfrancoisforce-pushed the ARROW-6532/write_parquet_compression branch from 354d263 to 50555f8CompareSeptember 27, 2019 14:04

@nealrichardsonnealrichardson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final style question but otherwise LGTM, happy to merge today regardless of where we land on the whitespace question.

Comment threadr/R/parquet.R
as_data_frame = TRUE,
props = ParquetReaderProperties$create(),
...) {
col_select = NULL,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is "bad", according to the tidyverse style guide, which I believed we were trying to follow: https://style.tidyverse.org/functions.html#long-lines-1

I can get used to whatever style conventions we decide, just want to make sure we're in agreement.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll setup my Rstudio to obey the style, perhaps we should use styler:: once in a while to do that automatically.

@wesm

wesm commented Sep 27, 2019

Copy link
Copy Markdown
Member

Can you update the PR description to reflect what is actually in the PR (since writing a Parquet file into a CompressedOutputStream isn't recommendable -- you would have to decompress the entire file first to be able to read any part of it)

@romainfrancoisromainfrancois changed the title ARROW-6532 [R] Write parquet files with compressionARROW-6532 [R] write_parquet() uses writer properties (general and arrow specific)Sep 27, 2019
nealrichardson pushed a commit that referenced this pull request Jan 8, 2020
The ability to preserve categorical values was introduced in #5077 as the convention of storing a special `ARROW:schema` key in the metadata. To invoke this, we need to call `ArrowWriterProperties::store_schema()`.
The R binding is already ready for this, but calls `store_schema()` only conditionally and uses `parquet___default_arrow_writer_properties()` by default. Though I don't see the motivation to implement as such in #5451, considering [the Python binding always calls `store_schema()`](https://github.com/apache/arrow/blob/dbe708c7527a4aa6b63df7722cd57db4e0bd2dc7/python/pyarrow/_parquet.pyx#L1269), I guess the R code can do the same.
Closes#6135 from yutannihilation/ARROW-7045_preserve_factor_in_parquet and squashes the following commits:
9227e7e <Hiroaki Yutani> Fix test
4d8bb46 <Hiroaki Yutani> Remove default_arrow_writer_properties()
dfd08cb <Hiroaki Yutani> Add failing tests
Authored-by: Hiroaki Yutani <yutani.ini@gmail.com>
Signed-off-by: Neal Richardson <neal.p.richardson@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@romainfrancois@nealrichardson@wesm@codecov-io@fsaintjacques