40 return owner->make_row_proxy(row_index);
46 return owner->make_const_row_proxy(row_index);
58 "DataFrame<KeyType> requires KeyType to be hashable (std::hash<KeyType> specialization required)."
63 "DataFrame<KeyType> requires KeyType to be equality comparable (operator== required)."
67 std::is_default_constructible<KeyType>::value,
68 "DataFrame<KeyType> requires KeyType to be default-constructible."
80#if CSV_EXCEPTIONS_ENABLED
83 this->init_unkeyed_from_reader(reader, error);
84 internals::throw_dataframe_error(error);
92#if CSV_EXCEPTIONS_ENABLED
99 this->init_unkeyed_from_reader(reader, error);
100 internals::throw_dataframe_error(error);
106 this->init_unkeyed_from_rows(rows);
113#if CSV_EXCEPTIONS_ENABLED
116 this->init_from_reader(reader, options, error);
117 internals::throw_dataframe_error(error);
125#if CSV_EXCEPTIONS_ENABLED
133 this->init_from_reader(reader, options, error);
134 internals::throw_dataframe_error(error);
142#if CSV_EXCEPTIONS_ENABLED
145 const std::string& _key_column,
147 bool throw_on_missing_key =
true
151 .set_key_column(_key_column)
152 .set_duplicate_key_policy(policy)
153 .set_throw_on_missing_key(throw_on_missing_key)
161#if CSV_EXCEPTIONS_ENABLED
164 csv::enable_if_t<csv::is_invocable_returning<KeyFunc, KeyType, const CSVRow&>::value,
int> = 0
172 this->init_custom_from_reader(reader, key_func, policy, error);
173 internals::throw_dataframe_error(error);
179 csv::enable_if_t<csv::is_invocable_returning<KeyFunc, KeyType, const CSVRow&>::value,
int> = 0
185 ) :
DataFrame(reader, key_func, options.get_duplicate_key_policy()) {}
191 std::unique_ptr<DataFrame> result(
new DataFrame());
192 if (!result->init_unkeyed_from_reader(reader, error))
return nullptr;
199 std::unique_ptr<DataFrame> result(
new DataFrame());
200 if (!result->init_from_reader(reader, options, error))
return nullptr;
209 return reader ?
try_create(*reader, error) : nullptr;
213 csv::string_view filename,
const DataFrameOptions& options, CSVError& error,
217 return reader ?
try_create(*reader, options, error) : nullptr;
220 template<
typename KeyFunc,
221 csv::enable_if_t<csv::is_invocable_returning<KeyFunc, KeyType, const CSVRow&>::value,
int> = 0>
223 CSVReader& reader, KeyFunc key_func, CSVError& error,
224 DuplicateKeyPolicy policy = DuplicateKeyPolicy::OVERWRITE
226 std::unique_ptr<DataFrame> result(
new DataFrame());
227 if (!result->init_custom_from_reader(reader, key_func, policy, error))
return nullptr;
231 template<
typename KeyFunc,
232 csv::enable_if_t<csv::is_invocable_returning<KeyFunc, KeyType, const CSVRow&>::value,
int> = 0>
234 CSVReader& reader, KeyFunc key_func,
const DataFrameOptions& options, CSVError& error
236 return try_create(reader, std::move(key_func), error, options.get_duplicate_key_policy());
241 return storage_.rows().size();
246 return storage_.rows().empty();
250 size_t n_rows() const noexcept {
return storage_.rows().size(); }
253 size_t n_cols() const noexcept {
return storage_.n_cols(); }
262 return this->storage_.names()->index_of(name);
266 const std::vector<std::string>&
columns() const noexcept {
return this->storage_.names()->get_col_names(); }
269#if CSV_EXCEPTIONS_ENABLED
270 void insert_row(
size_t index,
const std::vector<std::string>& row) {
273 internals::throw_dataframe_error(error);
280#if CSV_EXCEPTIONS_ENABLED
290#if CSV_EXCEPTIONS_ENABLED
291 void insert_column(
size_t index,
const std::string& name,
const std::string& default_value) {
294 internals::throw_dataframe_error(error);
299#if CSV_EXCEPTIONS_ENABLED
306#if CSV_EXCEPTIONS_ENABLED
307 void append_column(
const std::string& name,
const std::string& default_value) {
318#if CSV_EXCEPTIONS_ENABLED
322 internals::throw_dataframe_error(error);
330 if (index > this->
size())
332 CSVRow inserted_row = this->storage_.make_inserted_row(row, error);
333 if (error)
return false;
334 if (this->is_keyed_) {
337 CSVField field = inserted_row.
try_field(
static_cast<size_t>(this->key_column_index_), error);
338 if (error)
return false;
341 if (!field.
try_get(key, conversion)) {
344 this->ensure_key_index();
345 if (this->key_index_->find(key) != this->key_index_->end())
347 this->storage_.reserve_insert();
348 reserve_for_append(this->keys_, 1);
349 this->keys_.insert(this->keys_.begin() + index, std::move(key));
350 }
else { this->storage_.reserve_insert(); }
351 this->storage_.insert_row(index, std::move(inserted_row));
352 this->invalidate_key_index();
358 if (!this->storage_.insert_column(index, name, value, this->key_column_index_, [
this](
CSVError& failure) {
359 return !this->is_keyed_ || this->key_storage_.preserve(
360 this->keys_, [this]() { this->invalidate_key_index(); }, failure);
361 }, error))
return false;
362 this->invalidate_column_proxies();
363 this->invalidate_json_converter();
366 bool try_insert_column(
size_t index,
const std::string& name, CSVError& error) {
367 return this->try_insert_column(index, name, std::string(), error);
369 bool try_append_column(
const std::string& name, CSVError& error) {
370 return this->try_insert_column(this->n_cols(), name, error);
372 bool try_append_column(
const std::string& name,
const std::string& value, CSVError& error) {
373 return this->try_insert_column(this->n_cols(), name, value, error);
379 result.storage_ = this->storage_.selected_rows(mask, error);
384#if CSV_EXCEPTIONS_ENABLED
387 auto result = this->try_column_view(col_index, error);
388 internals::throw_dataframe_error(error);
394#if CSV_EXCEPTIONS_ENABLED
397 auto result = this->try_column_view(col_index, error);
398 internals::throw_dataframe_error(error);
404#if CSV_EXCEPTIONS_ENABLED
407 auto result = this->try_column_view(name, error);
408 internals::throw_dataframe_error(error);
414#if CSV_EXCEPTIONS_ENABLED
417 auto result = this->try_column_view(name, error);
418 internals::throw_dataframe_error(error);
432#if CSV_EXCEPTIONS_ENABLED
433 template<
typename K = KeyType,
434 csv::enable_if_t<!std::is_integral<K>::value,
int> = 0>
436 static_assert(std::is_same<K, KeyType>::value,
437 "Do not explicitly instantiate this template. Use at(size_t) for positional access.");
443 template<
typename K = KeyType,
444 csv::enable_if_t<!std::is_integral<K>::value,
int> = 0>
446 static_assert(std::is_same<K, KeyType>::value,
447 "Do not explicitly instantiate this template. Use at(size_t) for positional access.");
458#if CSV_EXCEPTIONS_ENABLED
461 auto result = this->try_at(i, error);
462 internals::throw_dataframe_error(error);
468#if CSV_EXCEPTIONS_ENABLED
471 auto result = this->try_at(i, error);
472 internals::throw_dataframe_error(error);
484#if CSV_EXCEPTIONS_ENABLED
487 auto result = this->try_find(key, error);
488 internals::throw_dataframe_error(error);
494#if CSV_EXCEPTIONS_ENABLED
497 auto result = this->try_find(key, error);
498 internals::throw_dataframe_error(error);
508#if CSV_EXCEPTIONS_ENABLED
511 const bool result = this->try_contains(key, error);
512 internals::throw_dataframe_error(error);
524#if CSV_EXCEPTIONS_ENABLED
525 template<
typename T = std::
string>
526 std::vector<T>
column(
const std::string& name)
const {
528 auto result = this->
template try_column<T>(name, error);
529 internals::throw_dataframe_error(error);
536 if (!this->validate_row_index(index, error))
return {};
537 return this->make_row_proxy(index);
539 row_type try_at(
size_t index,
CSVError& error)
const {
540 if (!this->validate_row_index(index, error))
return {};
541 return this->make_const_row_proxy(index);
543 row_type try_find(
const KeyType& key, CSVError& error) {
544 const size_t index = this->try_position_of(key, error);
545 return error ? row_type() : this->make_row_proxy(index);
547 row_type try_find(
const KeyType& key, CSVError& error)
const {
548 const size_t index = this->try_position_of(key, error);
549 return error ? row_type() : this->make_const_row_proxy(index);
551 bool try_contains(
const KeyType& key, CSVError& error)
const {
553 if (!this->require_keyed_frame(error))
return false;
554 this->ensure_key_index();
555 return this->key_index_->find(key) != this->key_index_->end();
557 column_type try_column_view(
size_t index, CSVError& error) {
558 if (!this->validate_column_index(index, error))
return {};
559 return column_type(
this, index);
561 column_type try_column_view(
size_t index, CSVError& error)
const {
562 if (!this->validate_column_index(index, error))
return {};
563 return column_type(
this, index);
565 column_type try_column_view(
const std::string& name, CSVError& error) {
566 const size_t index = this->try_find_column(name, error);
567 return error ? column_type() : column_type(this, index);
569 column_type try_column_view(
const std::string& name, CSVError& error)
const {
570 const size_t index = this->try_find_column(name, error);
571 return error ? column_type() : column_type(this, index);
573 template<
typename T = std::
string>
574 std::vector<T> try_column(
const std::string& name, CSVError& error)
const {
575 const auto view = this->try_column_view(name, error);
576 return error ? std::vector<T>() : view.template try_to_vector<T>(error);
592#if CSV_EXCEPTIONS_ENABLED
593 template<
typename State,
typename Fn>
596 std::vector<State>& states,
600 this->try_column_parallel_apply(executor, states, std::forward<Fn>(fn), error);
601 internals::throw_dataframe_error(error);
605 template<
typename State,
typename Fn>
606 bool try_column_parallel_apply(
608 std::vector<State>& states,
613 if (states.size() != this->n_cols()) {
617 executor.parallel_for(this->n_cols(), [
this, &states, &fn](
size_t column_index) {
618 fn(column_type(
this, column_index), states[column_index]);
631#if CSV_EXCEPTIONS_ENABLED
632 template<
typename State,
typename Fn>
635 const std::vector<size_t>& column_indices,
636 std::vector<State>& states,
640 this->try_column_parallel_apply(executor, column_indices, states, std::forward<Fn>(fn), error);
641 internals::throw_dataframe_error(error);
645 template<
typename State,
typename Fn>
646 bool try_column_parallel_apply(
648 const std::vector<size_t>& column_indices,
649 std::vector<State>& states,
654 if (states.size() != column_indices.size()) {
658 if (!this->validate_selected_columns(column_indices, error))
return false;
660 executor.parallel_for(column_indices.size(), [
this, &column_indices, &states, &fn](
size_t selected_index) {
661 const size_t column_index = column_indices[selected_index];
662 fn(column_type(this, column_index), states[selected_index]);
673#if CSV_EXCEPTIONS_ENABLED
674 template<
typename Fn>
680 this->try_column_parallel_apply(executor, std::forward<Fn>(fn), error);
681 internals::throw_dataframe_error(error);
685 template<
typename Fn>
686 bool try_column_parallel_apply(
692 executor.parallel_for(this->n_cols(), [
this, &fn](
size_t column_index) {
693 fn(column_type(
this, column_index));
705#if CSV_EXCEPTIONS_ENABLED
706 template<
typename Fn>
709 const std::vector<size_t>& column_indices,
713 this->try_column_parallel_apply(executor, column_indices, std::forward<Fn>(fn), error);
714 internals::throw_dataframe_error(error);
718 template<
typename Fn>
719 bool try_column_parallel_apply(
721 const std::vector<size_t>& column_indices,
726 if (!this->validate_selected_columns(column_indices, error))
return false;
728 executor.parallel_for(column_indices.size(), [
this, &column_indices, &fn](
size_t selected_index) {
729 fn(column_type(this, column_indices[selected_index]));
741 typename GroupKey = invoke_result_t<GroupFunc, DataFrameRow<KeyType>>,
743 internals::is_hashable<GroupKey>::value &&
744 internals::is_equality_comparable<GroupKey>::value,
748 std::unordered_map<GroupKey, std::vector<size_t>>
group_by(GroupFunc group_func)
const {
749 std::unordered_map<GroupKey, std::vector<size_t>> grouped;
751 for (
size_t i = 0; i < storage_.rows().size(); i++) {
752 GroupKey group_key = group_func(this->make_const_row_proxy(i));
753 grouped[group_key].push_back(i);
764#if CSV_EXCEPTIONS_ENABLED
765 std::unordered_map<std::string, std::vector<size_t>>
group_by(
const std::string& name)
const {
767 auto result = this->try_group_by(name, error);
768 internals::throw_dataframe_error(error);
774 std::unordered_map<std::string, std::vector<size_t>>
try_group_by(
const std::string& name,
CSVError& error)
const {
775 std::unordered_map<std::string, std::vector<size_t>> grouped;
776 const size_t column = this->try_find_column(name, error);
777 if (error)
return grouped;
778 for (
size_t i = 0; i < this->size(); ++i) {
779 const auto cell = this->try_make_cell(i, column,
false, error);
780 if (error)
return {};
781 grouped[std::string(cell.get_sv())].push_back(i);
806 bool is_keyed_ =
false;
811 std::vector<KeyType> keys_;
818 mutable std::unique_ptr<std::unordered_map<KeyType, size_t>> key_index_;
820 std::shared_ptr<size_t> column_generation_{
new size_t(0) };
823 bool init_unkeyed_from_reader(CSVReader& reader, CSVError& error) {
825 this->assert_fresh_storage(
false);
826 this->storage_.set_names(reader.col_names_ptr());
827 std::vector<CSVRow> batch;
828 while (reader.try_read_chunk(batch, dataframe_read_chunk_rows(), error)) {
829 this->append_unkeyed_batch(batch);
835 void init_unkeyed_from_rows(std::vector<CSVRow>& source_rows) {
836 this->assert_fresh_storage(
false);
837 this->is_keyed_ =
false;
838 this->storage_.set_names(source_rows.empty()
839 ? internals::ConstColNamesPtr(std::make_shared<internals::ColNames>())
840 : source_rows.front().col_names_ptr());
841 this->storage_.rows() = std::move(source_rows);
845 bool init_from_reader(CSVReader& reader,
const DataFrameOptions& options, CSVError& error) {
847 this->assert_fresh_storage(
false);
848 this->is_keyed_ =
true;
849 this->storage_.set_names(reader.col_names_ptr());
850 const std::string& name = options.get_key_column();
853 const int index = this->storage_.names()->index_of(name);
856 this->key_column_index_ = index;
857 auto extract = [&options, &name](
const CSVRow& row, KeyType& key, CSVError& failure) {
858 CSVField field = row.try_field(name, failure);
860 if (!failure && field.try_get(key, conversion))
return true;
861 if (!options.get_throw_on_missing_key()) {
869 failure.set_context(std::string(internals::ERROR_KEY_COLUMN_VALUE) + std::string(failure.context));
872 return this->build_from_key_function(reader, extract, options.get_duplicate_key_policy(), error);
875 template<
typename KeyFunc>
876 bool init_custom_from_reader(CSVReader& reader, KeyFunc& key_func, DuplicateKeyPolicy policy, CSVError& error) {
877 this->storage_.set_names(reader.col_names_ptr());
878 this->is_keyed_ =
true;
879 auto extract = [&key_func](
const CSVRow& row, KeyType& key, CSVError&) {
883 return this->build_from_key_function(reader, extract, policy, error);
887 template<
typename KeyFunc>
888 bool build_from_key_function(
889 CSVReader& reader, KeyFunc& key_func, DuplicateKeyPolicy policy, CSVError& error
892 std::unordered_map<KeyType, size_t> key_to_pos;
893 this->assert_fresh_storage(
true);
894 std::vector<CSVRow> batch;
895 while (reader.try_read_chunk(batch, dataframe_read_chunk_rows(), error)) {
896 if (!this->append_keyed_batch(batch, key_func, policy, key_to_pos, error))
return false;
901 static size_t dataframe_read_chunk_rows() noexcept {
905 template<
typename Container>
906 static void reserve_for_append(Container& container,
size_t additional) {
907 if (additional == 0) {
911 const size_t required = container.size() + additional;
912 if (required <= container.capacity()) {
916 const size_t current = container.capacity();
920 size_t next = current == 0 ? additional : current * 2;
921 if (next < required || next < current) {
925 container.reserve(next);
928 void append_unkeyed_batch(std::vector<CSVRow>& batch) {
929 reserve_for_append(storage_.rows(), batch.size());
931 for (
auto& row : batch) {
932 storage_.rows().push_back(std::move(row));
936 template<
typename KeyFunc>
937 bool append_keyed_batch(
938 std::vector<CSVRow>& batch, KeyFunc& key_func, DuplicateKeyPolicy policy,
939 std::unordered_map<KeyType, size_t>& key_to_pos, CSVError& error
941 reserve_for_append(storage_.rows(), batch.size());
942 reserve_for_append(keys_, batch.size());
943 for (
auto& row : batch) {
945 if (!key_func(row, key, error))
return false;
946 auto existing = key_to_pos.find(key);
947 if (existing != key_to_pos.end()) {
948 if (policy == DuplicateKeyPolicy::THROW)
950 if (policy == DuplicateKeyPolicy::OVERWRITE)
951 storage_.rows()[existing->second] = std::move(row);
954 storage_.rows().push_back(std::move(row));
955 keys_.push_back(key);
956 key_to_pos[key] = storage_.rows().size() - 1;
962 size_t try_find_column(
const std::string& name, CSVError& error)
const {
964 const int index = this->index_of(name);
969 return static_cast<size_t>(index);
972 bool validate_row_index(
size_t index, CSVError& error)
const {
976 bool validate_column_index(
size_t index, CSVError& error)
const {
980 DataFrameCell try_make_cell(
size_t row_index,
size_t column_index,
bool mutable_access, CSVError& error)
const {
981 const auto binding = this->storage_.try_bind(row_index, column_index, error);
982 if (error)
return {};
983 const auto* row = &this->storage_.rows()[row_index];
984 return mutable_access
985 ? DataFrameCell(row, binding.overlay, binding.physical_column, &this->storage_,
true)
986 : DataFrameCell(row, binding.overlay, binding.physical_column);
989 DataFrameCell make_cell(
size_t row_index,
size_t column_index,
bool mutable_access)
const {
990 return this->make_cell(&this->storage_.rows()[row_index], row_index, column_index, mutable_access);
993 DataFrameCell make_cell(
const CSVRow* row,
size_t row_index,
size_t column_index,
bool mutable_access)
const {
994 const auto binding = this->storage_.bind(row_index, column_index);
995 return mutable_access
996 ? DataFrameCell(row, binding.overlay, binding.physical_column, &this->storage_,
true)
997 : DataFrameCell(row, binding.overlay, binding.physical_column);
1001 return this->storage_.view(row_index, column_index);
1004 DataFrameRow<KeyType> make_row_proxy(
size_t row_index) {
1005 const auto& row = storage_.rows()[row_index];
1006 return DataFrameRow<KeyType>(&row,
this, row_index,
static_cast<RowOverlay*
>(
nullptr), this->key_ptr_at(row_index));
1009 DataFrameRow<KeyType> make_const_row_proxy(
size_t row_index)
const {
1010 const auto& row = storage_.rows()[row_index];
1011 return DataFrameRow<KeyType>(&row,
this, row_index,
static_cast<const RowOverlay*
>(
nullptr), this->key_ptr_at(row_index));
1014 bool erase_column_at_index(
size_t column_index, CSVError& error) {
1015 if (!this->storage_.erase_column(column_index, this->key_column_index_, error))
return false;
1016 this->invalidate_column_proxies();
1017 this->invalidate_json_converter();
1021 bool erase_at_index(
size_t row_index) {
1022 if (row_index >= storage_.rows().size()) {
1026 this->storage_.erase_row(row_index);
1027 if (this->is_keyed_) {
1028 keys_.erase(keys_.begin() + row_index);
1030 this->invalidate_key_index();
1034 bool validate_selected_columns(
const std::vector<size_t>& column_indices, CSVError& error)
const {
1036 for (
size_t index : column_indices) {
1037 if (index >= this->n_cols())
1044 bool require_keyed_frame(CSVError& error)
const {
1045 if (!this->is_keyed_)
1051 void invalidate_key_index() {
1056 void invalidate_json_converter() {
1057 json_converter_ = internals::lazy_shared_ptr<internals::JsonConverter>();
1061 void invalidate_column_proxies() {
1062 ++(*column_generation_);
1066 void assert_fresh_storage(
bool expected_is_keyed)
const {
1067 CSV_DEBUG_ASSERT(this->storage_.rows().empty());
1068 CSV_DEBUG_ASSERT(this->keys_.empty());
1069 CSV_DEBUG_ASSERT(this->storage_.pristine());
1070 CSV_DEBUG_ASSERT(this->key_index_.get() ==
nullptr);
1071 CSV_DEBUG_ASSERT(this->json_converter_.get() ==
nullptr);
1072 CSV_DEBUG_ASSERT(this->is_keyed_ == expected_is_keyed);
1076 void ensure_key_index()
const {
1077 if (key_index_)
return;
1079 key_index_ = std::unique_ptr<std::unordered_map<KeyType, size_t>>(
1080 new std::unordered_map<KeyType, size_t>()
1083 for (
size_t i = 0; i < storage_.rows().size(); i++) {
1084 (*key_index_)[keys_[i]] = i;
1089 size_t try_position_of(
const KeyType& key, CSVError& error)
const {
1091 if (!this->require_keyed_frame(error))
return 0;
1092 this->ensure_key_index();
1093 const auto found = this->key_index_->find(key);
1094 if (found == this->key_index_->end()) {
1098 return found->second;
1101 const KeyType* key_ptr_at(
size_t row_index)
const {
1102 return this->is_keyed_ ? &keys_[row_index] :
nullptr;