Skip to content

Commit

Permalink
Consolidate DataFrames extension code to ensure relocatability (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
JackDunnNZ authored Jun 2, 2023
1 parent 18852c2 commit 72bdb32
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 86 deletions.
87 changes: 86 additions & 1 deletion ext/BangBangDataFramesExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,93 @@ else
using ..DataFrames
using ..DataFrames.Tables: Tables
end
include("dataframes_impl.jl")
BangBang.push!!(df::DataFrames.DataFrame, row) = df_append_rows!!(df, (row,))
BangBang.append!!(df::DataFrames.DataFrame, source) = df_append!!(df, source)
BangBang.copyappendable(df::DataFrames.AbstractDataFrame) = copy(df)

_getvalue(x, pos, name) = getproperty(x, name)
_getvalue(x::AbstractVector, pos, name) = x[pos]
_getvalue(x::Tuple, pos, name) = x[pos]
_getvalue(x::NamedTuple, pos, name) = x[name]
_getvalue(x::AbstractDict, pos, name) = x[name]

_hascolumn(x, n) = hasproperty(x, n)
_hascolumn(::NamedTuple{names}, n) where {names} = n in names # optimization
_hascolumn(x::AbstractDict, n) = haskey(x, n)

ncolumns(x) = length(propertynames(x)) # e.g., Tables.IteratorRow
ncolumns(x::Union{NamedTuple, AbstractDict}) = length(x)

function checkcolumnnames(x, columnnames)
for n in columnnames
_hascolumn(x, n) || error("No column `", n, "` in given row.")
end
nc = ncolumns(x)
length(columnnames) < nc && error("More columns exist in given row.")
@assert length(columnnames) == nc
end

function checkcolumnnames(x::Union{Tuple,AbstractVector}, columnnames)
length(columnnames) == length(x) || error("Number of columns does not match.")
end

function df_append_columns!!(df, table)
columns = getfield(df, :columns)
colnames = DataFrames._names(df) # avoid copy
checkcolumnnames(columns, colnames)
for (pos, (name, col)) in enumerate(zip(colnames, columns))
columns[pos] = BangBang.append!!(col, _getvalue(table, pos, name))
end
return df
end

macro manually_specialize(expr, head, tail...)
Expr(:if, head, expr, manually_specialize_impl(expr, tail)) |> esc
end

manually_specialize_impl(expr, predicates) =
if isempty(predicates)
expr
else
Expr(
:elseif,
predicates[1],
expr,
manually_specialize_impl(expr, predicates[2:end]),
)
end

function df_append_rows!!(df, table)
columns = getfield(df, :columns)
colnames = DataFrames._names(df) # avoid copy
# colnames = propertynames(df)
for x in table
checkcolumnnames(x, colnames)
for (pos, (name, col)) in enumerate(zip(colnames, columns))
v = _getvalue(x, pos, name)
@manually_specialize(
columns[pos] = BangBang.push!!(col, v),
# "Inline" some method lookups for typical column types:
col isa Vector{Int},
col isa Vector{Union{Int,Missing}},
col isa Vector{Float64},
col isa Vector{Union{Float64,Missing}},
col isa Vector{Symbol},
col isa Vector{Union{Symbol,Missing}},
col isa Vector{String},
col isa Vector{Union{String,Missing}},
)
end
end
return df
end

df_append!!(df, table) =
if Tables.columnaccess(table)
df_append_columns!!(df, Tables.columns(table))
elseif Tables.rowaccess(table)
df_append_rows!!(df, Tables.rows(table))
else
df_append_rows!!(df, table)
end
end
85 changes: 0 additions & 85 deletions ext/dataframes_impl.jl

This file was deleted.

0 comments on commit 72bdb32

Please sign in to comment.