diff --git a/src/DataFrames.jl b/src/DataFrames.jl index 4df4b26750..03f024ba02 100644 --- a/src/DataFrames.jl +++ b/src/DataFrames.jl @@ -52,6 +52,7 @@ export AbstractDataFrame, GroupedDataFrame, SubDataFrame, Tables, + @alias, allcombinations, allowmissing!, antijoin, @@ -132,6 +133,7 @@ const METADATA_FIXED = include("other/utils.jl") include("other/index.jl") +include("other/alias.jl") include("abstractdataframe/abstractdataframe.jl") include("dataframe/dataframe.jl") diff --git a/src/abstractdataframe/abstractdataframe.jl b/src/abstractdataframe/abstractdataframe.jl index 1dda8c8765..22eae1d901 100644 --- a/src/abstractdataframe/abstractdataframe.jl +++ b/src/abstractdataframe/abstractdataframe.jl @@ -3223,7 +3223,7 @@ function insertcols!(df::AbstractDataFrame, col::ColumnIndex, name_cols::Pair{Sy firstindex(item_new) != 1 && _onebased_check_error() if ncol(dfp) == 0 - dfp[!, name] = item_new + @alias dfp[!, name] = item_new else if hasproperty(dfp, name) @assert makeunique diff --git a/src/abstractdataframe/selection.jl b/src/abstractdataframe/selection.jl index 0d1363fe89..4db0df1c00 100644 --- a/src/abstractdataframe/selection.jl +++ b/src/abstractdataframe/selection.jl @@ -682,13 +682,13 @@ function _add_col_check_copy(newdf::DataFrame, df::AbstractDataFrame, @assert parent_cols isa Union{Int, AbstractVector{Int}} if copycols if !(fun isa ByRow) && (v isa SubArray || any(i -> vpar === parent(cdf[i]), parent_cols)) - newdf[!, newname] = copy(v) - else newdf[!, newname] = v + else + @alias newdf[!, newname] = v end else if fun isa ByRow - newdf[!, newname] = v + @alias newdf[!, newname] = v else must_copy = false for i in parent_cols @@ -702,7 +702,11 @@ function _add_col_check_copy(newdf::DataFrame, df::AbstractDataFrame, end end end - newdf[!, newname] = must_copy ? copy(v) : v + if must_copy + newdf[!, newname] = v + else + @alias newdf[!, newname] = v + end end end end @@ -807,7 +811,7 @@ function select_transform!((nc,)::Ref{Any}, df::AbstractDataFrame, newdf::DataFr end newres = DataFrame() for n in kp1 - newres[!, prepend ? Symbol("x", n) : Symbol(n)] = [x[n] for x in res] + @alias newres[!, prepend ? Symbol("x", n) : Symbol(n)] = [x[n] for x in res] end res = newres elseif !(res isa Union{AbstractDataFrame, NamedTuple, DataFrameRow, AbstractMatrix}) @@ -872,7 +876,7 @@ function select_transform!((nc,)::Ref{Any}, df::AbstractDataFrame, newdf::DataFr # allow squashing a scalar to 0 rows rows = nrow(newdf) end - newdf[!, newname] = fill!(Tables.allocatecolumn(typeof(res_unwrap), rows), + @alias newdf[!, newname] = fill!(Tables.allocatecolumn(typeof(res_unwrap), rows), res_unwrap) if (col_idx isa Int || (col_idx isa AbstractVector{Int} && length(col_idx) == 1)) && (fun === identity || fun === copy || _names(df)[only(col_idx)] == newname) @@ -1756,7 +1760,7 @@ function _manipulate(df::AbstractDataFrame, normalized_cs::Vector{Any}, copycols end end # here even if keeprows is true all is OK - newdf[!, newname] = column_to_copy[i] ? df[:, i] : df[!, i] + @alias newdf[!, newname] = column_to_copy[i] ? df[:, i] : df[!, i] column_to_copy[i] = true allow_resizing_newdf[] = false _copy_col_note_metadata!(newdf, newname, df, i) diff --git a/src/dataframe/dataframe.jl b/src/dataframe/dataframe.jl index 12036fe954..941a9dd113 100755 --- a/src/dataframe/dataframe.jl +++ b/src/dataframe/dataframe.jl @@ -232,7 +232,7 @@ end function _preprocess_column(col::Any, len::Integer, copycols::Bool) if col isa AbstractRange - return collect(col) + return copycols ? collect(col) : col elseif col isa AbstractVector return copycols ? copy(col) : col elseif col isa Union{AbstractArray{<:Any, 0}, Ref} @@ -632,11 +632,11 @@ Base.getindex(df::DataFrame, row_ind::typeof(!), col_inds::MultiColumnIndex) = ############################################################################## # Will automatically add a new column if needed -function insert_single_column!(df::DataFrame, v::AbstractVector, col_ind::ColumnIndex) - if ncol(df) != 0 && nrow(df) != length(v) - throw(ArgumentError("New columns must have the same length as old columns")) +function insert_single_column!(df::DataFrame, v::Any, col_ind::ColumnIndex; copycols = true) + dv = _preprocess_column(v, nrow(df), copycols) + if ncol(df) != 0 && nrow(df) != length(dv) + throw(DimensionMismatch("Length of the column ($(length(dv))) and rows in the data frame ($(nrow(df))) are not equal")) end - dv = isa(v, AbstractRange) ? collect(v) : v firstindex(dv) != 1 && _onebased_check_error() if haskey(index(df), col_ind) @@ -665,23 +665,27 @@ function insert_single_entry!(df::DataFrame, v::Any, row_ind::Integer, col_ind:: end # df[!, SingleColumnIndex] = AbstractVector -function Base.setindex!(df::DataFrame, v::AbstractVector, ::typeof(!), col_ind::ColumnIndex) +function Base.setindex!(df::DataFrame, v::AbstractVector, ::typeof(!), col_ind::ColumnIndex; copycols = true) + insert_single_column!(df, v, col_ind; copycols) + return df +end + +# df[!, SingleColumnIndex] = value +function Base.setindex!(df::DataFrame, v::Any, ::typeof(!), col_ind::ColumnIndex) insert_single_column!(df, v, col_ind) return df end -# df.col = AbstractVector +# df.col = value # separate methods are needed due to dispatch ambiguity -Base.setproperty!(df::DataFrame, col_ind::Symbol, v::AbstractVector) = +Base.setproperty!(df::DataFrame, col_ind::Symbol, v::AbstractVector; copycols = true) = + setindex!(df, v, !, col_ind; copycols) +Base.setproperty!(df::DataFrame, col_ind::AbstractString, v::AbstractVector; copycols = true) = + setindex!(df, v, !, col_ind; copycols) +Base.setproperty!(df::DataFrame, col_ind::Symbol, v::Any) = (df[!, col_ind] = v) -Base.setproperty!(df::DataFrame, col_ind::AbstractString, v::AbstractVector) = +Base.setproperty!(df::DataFrame, col_ind::AbstractString, v::Any) = (df[!, col_ind] = v) -Base.setproperty!(::DataFrame, col_ind::Symbol, v::Any) = - throw(ArgumentError("It is only allowed to pass a vector as a column of a DataFrame. " * - "Instead use `df[!, col_ind] .= v` if you want to use broadcasting.")) -Base.setproperty!(::DataFrame, col_ind::AbstractString, v::Any) = - throw(ArgumentError("It is only allowed to pass a vector as a column of a DataFrame. " * - "Instead use `df[!, col_ind] .= v` if you want to use broadcasting.")) # df[SingleRowIndex, SingleColumnIndex] = Single Item function Base.setindex!(df::DataFrame, v::Any, row_ind::Integer, col_ind::ColumnIndex) @@ -718,7 +722,7 @@ for T in (:AbstractVector, :Not, :Colon) row_inds::$T, col_ind::ColumnIndex) if row_inds isa Colon && !haskey(index(df), col_ind) - df[!, col_ind] = copy(v) + df[!, col_ind] = v return df end x = df[!, col_ind] @@ -786,6 +790,28 @@ for T1 in (:AbstractVector, :Not, :Colon, :(typeof(!))), end end +for T1 in (:(typeof(!)),), + T2 in MULTICOLUMNINDEX_TUPLE + @eval function Base.setindex!(df::DataFrame, + v::AbstractVector, + row_inds::$T1, + col_inds::$T2) + throw(ArgumentError("a vector can not be assigned to multiple rows and columns, consider reshaping to a matrix first")) + end + + @eval function Base.setindex!(df::DataFrame, + v::Any, + row_inds::$T1, + col_inds::$T2) + idxs = index(df)[col_inds] + for col in idxs + # this will drop metadata appropriately + df[row_inds, col] = v + end + return df + end +end + """ copy(df::DataFrame; copycols::Bool=true) @@ -1191,7 +1217,11 @@ function hcat!(df1::DataFrame, df2::AbstractDataFrame; _drop_all_nonnote_metadata!(df1) _keep_matching_table_note_metadata!(df1, df2) for i in 1:length(u) - df1[!, u[i]] = copycols ? df2[:, i] : df2[!, i] + if copycols + df1[!, u[i]] = df2[!, i] + else + @alias df1[!, u[i]] = df2[!, i] + end _copy_col_note_metadata!(df1, u[i], df2, i) end diff --git a/src/other/alias.jl b/src/other/alias.jl new file mode 100644 index 0000000000..71efbe8ff3 --- /dev/null +++ b/src/other/alias.jl @@ -0,0 +1,31 @@ +""" +@alias df.col = v +@alias df[!, :col] = v + +df::DataFrame +v::AbstractVector + +Assigns v to the column `col` without copying. Such that `df.col === v === df]!, :col]```. +Any AbstractVector is permissable, this may limit what operations are possible to do on `df` afterwards. +For instance after `@alias df.col = 1:3` it won't be possible to change the number of rows because UnitRange does not support resizing. +""" +macro alias(ex) + if !Meta.isexpr(ex, :(=)) + throw(ArgumentError("Invalid use of @alias macro: argument must be a column assignment `df.col = v` or `df[!, col] = v`")) + end + + lhs, rhs = ex.args + + if Meta.isexpr(lhs, :ref) + ex = :(setindex!($(lhs.args[1]), $rhs, $(lhs.args[2:end]...); copycols = false); $rhs) + + elseif Meta.isexpr(lhs, :.) + ex = :(setproperty!($(lhs.args...), $rhs; copycols = false); $rhs) + + else + throw(ArgumentError("Invalid use of @alias macro: argument must be a column assignment `df.col = v` or `df[!, col] = v`")) + + end + + esc(ex) +end diff --git a/src/other/broadcasting.jl b/src/other/broadcasting.jl index f882c4c758..01640d7829 100644 --- a/src/other/broadcasting.jl +++ b/src/other/broadcasting.jl @@ -153,6 +153,7 @@ function Base.maybeview(df::AbstractDataFrame, rows, cols) return view(df, rows, cols) end +# df[:, cols] .= ... function Base.dotview(df::AbstractDataFrame, ::Colon, cols::ColumnIndex) if haskey(index(df), cols) _drop_all_nonnote_metadata!(parent(df)) @@ -168,28 +169,20 @@ function Base.dotview(df::AbstractDataFrame, ::Colon, cols::ColumnIndex) return LazyNewColDataFrame(df, Symbol(cols)) end -function Base.dotview(df::AbstractDataFrame, ::typeof(!), cols) - if !(cols isa ColumnIndex) - return ColReplaceDataFrame(df, convert(Vector{Int}, index(df)[cols])) - end - if cols isa SymbolOrString - if columnindex(df, cols) == 0 && !is_column_insertion_allowed(df) - throw(ArgumentError("creating new columns in a SubDataFrame that subsets " * - "columns of its parent data frame is disallowed")) - end - elseif !(1 <= cols <= ncol(df)) - throw(ArgumentError("creating new columns using an integer index is disallowed")) - end - return LazyNewColDataFrame(df, cols isa AbstractString ? Symbol(cols) : cols) +# df[!, cols] .= ... +function Base.dotview(df::AbstractDataFrame, ::typeof(!), cols::Any) + return ColReplaceDataFrame(df, convert(Vector{Int}, index(df)[cols])) +end +function Base.dotview(df::AbstractDataFrame, ::typeof(!), cols::ColumnIndex) + _drop_all_nonnote_metadata!(parent(df)) + return df[!, cols] end if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 + # df.col .= ... function Base.dotgetproperty(df::AbstractDataFrame, col::SymbolOrString) - if columnindex(df, col) == 0 && !is_column_insertion_allowed(df) - throw(ArgumentError("creating new columns in a SubDataFrame that subsets " * - "columns of its parent data frame is disallowed")) - end - return LazyNewColDataFrame(df, Symbol(col)) + _drop_all_nonnote_metadata!(parent(df)) + return df[!, col] end end diff --git a/src/subdataframe/subdataframe.jl b/src/subdataframe/subdataframe.jl index e27f5a88d2..63c6f6cda1 100644 --- a/src/subdataframe/subdataframe.jl +++ b/src/subdataframe/subdataframe.jl @@ -190,8 +190,7 @@ Base.@propagate_inbounds function Base.setindex!(sdf::SubDataFrame, val::Any, :: "columns of its parent data frame is disallowed")) end if !(val isa AbstractVector && nrow(sdf) == length(val)) - throw(ArgumentError("Assigned value must be a vector with length " * - "equal to number of rows in the SubDataFrame")) + throw(DimensionMismatch("Length of the assigned column ($(length(val))) and rows in the SubDataFrame ($(nrow(sdf))) are not equal")) end T = eltype(val) newcol = similar(val, Union{T, Missing}, nrow(parent(sdf))) @@ -209,7 +208,7 @@ end # and then define methods for them) # consider merging SubDataFrame and DataFrame setindex! methods -function Base.setindex!(sdf::SubDataFrame, v::AbstractVector, +function Base.setindex!(sdf::SubDataFrame, v::Any, ::typeof(!), col_ind::ColumnIndex) if col_ind isa Union{Signed, Unsigned} && !(1 <= col_ind <= ncol(sdf)) throw(ArgumentError("Cannot assign to non-existent column: $col_ind")) @@ -219,15 +218,17 @@ function Base.setindex!(sdf::SubDataFrame, v::AbstractVector, throw(ArgumentError("creating new columns in a SubDataFrame that subsets " * "columns of its parent data frame is disallowed")) end + v = _preprocess_column(v, nrow(sdf), false) sdf[:, col_ind] = v else pdf = parent(sdf) p_col_ind = parentcols(index(sdf), col_ind) old_col = pdf[!, p_col_ind] T = eltype(old_col) - S = eltype(v) + S = v isa AbstractVector ? eltype(v) : typeof(v) newcol = similar(old_col, promote_type(T, S), length(old_col)) newcol .= old_col + v = _preprocess_column(v, nrow(sdf), false) newcol[rows(sdf)] = v pdf[!, p_col_ind] = newcol end @@ -278,12 +279,10 @@ Base.setproperty!(df::SubDataFrame, col_ind::Symbol, v::AbstractVector) = (df[!, col_ind] = v) Base.setproperty!(df::SubDataFrame, col_ind::AbstractString, v::AbstractVector) = (df[!, col_ind] = v) -Base.setproperty!(::SubDataFrame, col_ind::Symbol, v::Any) = - throw(ArgumentError("It is only allowed to pass a vector as a column of a SubDataFrame. " * - "Instead use `df[!, col_ind] .= v` if you want to use broadcasting.")) -Base.setproperty!(::SubDataFrame, col_ind::AbstractString, v::Any) = - throw(ArgumentError("It is only allowed to pass a vector as a column of a SubDataFrame. " * - "Instead use `df[!, col_ind] .= v` if you want to use broadcasting.")) +Base.setproperty!(df::SubDataFrame, col_ind::Symbol, v::Any) = + (df[!, col_ind] = v) +Base.setproperty!(df::SubDataFrame, col_ind::AbstractString, v::Any) = + (df[!, col_ind] = v) ############################################################################## ## diff --git a/test/alias.jl b/test/alias.jl new file mode 100644 index 0000000000..1c8ebb6b3c --- /dev/null +++ b/test/alias.jl @@ -0,0 +1,68 @@ +module TestAliasAssignment + +using Test, DataFrames +const ≅ = isequal + +@testset "Aliasing asignment" begin + @testset "$init" for init in [[], Matrix([4 5 6]'), [4 5;6 7;8 9]] + dfr = DataFrame(init, :auto) + @testset "$v" for v in [ + [1,2,3], + 1:3, + ] + @testset "df.x2 = v" begin + df = copy(dfr) + a = @alias df.x2 = v + @test a === v + @test df.x2 === df[!, :x2] === v + df.x2 = v + @test df.x2 !== v + @test df.x2 ≅ v + a = @alias df.x2 = v + @test a === v + @test df.x2 === df[!, :x2] === v + end + + @testset "df[!, :x2] = v" begin + df = copy(dfr) + a = @alias df[!, :x2] = v + @test a === v + @test df.x2 === df[!, :x2] === v + df[!, :x2] = v + @test df[!, :x2] !== v + @test df[!, :x2] ≅ v + a = @alias df[!, :x2] = v + @test a === v + @test df.x2 === df[!, :x2] === v + end + end + + @testset "Invalid use of alias macro" begin + ex = try + eval(:(@alias 1)) + catch ex + ex + end + @test ex.error isa ArgumentError + + df = copy(dfr) + ex = try + eval(:(@alias df.x .= 1)) + catch ex + ex + end + @test ex.error isa ArgumentError + + struct S; x; end + s = S(1) + @test_throws MethodError @alias s.x = 1 + @test_throws MethodError @alias s.x2 = 1 + + @test_throws MethodError @alias df.x2 = 1 + @test_throws MethodError @alias df[!, :x2] = 1 + @test_throws MethodError @alias df[:, :x2] = 1 + end + end +end + +end # module diff --git a/test/broadcasting.jl b/test/broadcasting.jl index 19d9e0e1a7..c1ba1d8417 100644 --- a/test/broadcasting.jl +++ b/test/broadcasting.jl @@ -755,30 +755,59 @@ end 10.0 10.0 10.0 10.0 10.0] end -@testset "extending data frame in broadcasted assignment - one column" begin +@testset "extending data frame in assignment - one column" begin df = copy(refdf) - df[!, :a] .= 1 + df[!, :a] = 1 @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 2.5 5.5 8.5 11.5 14.5 1.0 3.5 6.5 9.5 12.5 15.5 1.0] @test names(df)[end] == "a" @test df[:, 1:end-1] == refdf - df[!, :b] .= [1, 2, 3] + df[!, :b] = [1, 2, 3] @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 1.0 2.5 5.5 8.5 11.5 14.5 1.0 2.0 3.5 6.5 9.5 12.5 15.5 1.0 3.0] @test names(df)[end] == "b" @test df[:, 1:end-2] == refdf + + df = copy(refdf) + df.a = 1 + @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 + 2.5 5.5 8.5 11.5 14.5 1.0 + 3.5 6.5 9.5 12.5 15.5 1.0] + @test names(df)[end] == "a" + @test df[:, 1:end-1] == refdf + df.b = [1, 2, 3] + @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 1.0 + 2.5 5.5 8.5 11.5 14.5 1.0 2.0 + 3.5 6.5 9.5 12.5 15.5 1.0 3.0] + @test names(df)[end] == "b" + @test df[:, 1:end-2] == refdf + cdf = copy(df) - @test_throws DimensionMismatch df[!, :c] .= ones(1, 3) + @test_throws DimensionMismatch df[!, :a] .= ones(1, 3) + @test_throws DimensionMismatch df.a .= ones(1, 3) + @test_throws ArgumentError df[!, :c] .= ones(1, 3) + @test_throws ArgumentError df.c .= ones(1, 3) + @test df == cdf + @test_throws ArgumentError df[!, :d] .= 1 + @test_throws ArgumentError df.d .= 1 @test df == cdf - @test_throws DimensionMismatch df[!, :x] .= ones(4) + @test_throws DimensionMismatch df[!, :a] .= ones(4) + @test_throws DimensionMismatch df.a .= ones(4) + @test_throws ArgumentError df[!, :x] .= ones(4) + @test_throws DimensionMismatch df[!, :x] = ones(4) + @test_throws ArgumentError df.x .= ones(4) + @test_throws DimensionMismatch df.x = ones(4) + @test_throws ArgumentError df[!, :x] .= ones(4) @test df == cdf - @test_throws ArgumentError df[!, 10] .= ones(3) + @test_throws BoundsError df[!, 10] .= ones(3) + @test_throws ArgumentError df[!, 10] = ones(3) @test df == cdf dfv = @view df[1:2, 2:end] - @test_throws ArgumentError dfv[!, 10] .= ones(3) + @test_throws BoundsError dfv[!, 10] .= ones(3) + @test_throws ArgumentError dfv[!, 10] = ones(3) @test_throws ArgumentError dfv[!, :z] .= ones(3) @test df == cdf dfr = df[1, 3:end] @@ -786,35 +815,45 @@ end @test_throws ArgumentError dfr[:z] .= ones(3) @test df == cdf + df = DataFrame(x=Int[]) + @test_throws DimensionMismatch df[!, :a] = sin.(1:3) + df[!, :b] = sin.(1) + df[!, :c] = sin(1) .+ 1 + @test df == DataFrame(x=Int[], b=Float64[], c=Float64[]) + df = DataFrame() - @test_throws DimensionMismatch df[!, :a] .= sin.(1:3) - df[!, :b] .= sin.(1) - df[!, :c] .= sin(1) .+ 1 - @test df == DataFrame(b=Float64[], c=Float64[]) + df[!, :a] = sin.(1:3) + df[!, :b] = sin.(1) + df[!, :c] = sin(1) .+ 1 + @test size(df) == (3, 3) df = copy(refdf) - df[!, "a"] .= 1 + df[!, "a"] = 1 @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 2.5 5.5 8.5 11.5 14.5 1.0 3.5 6.5 9.5 12.5 15.5 1.0] @test names(df)[end] == "a" @test df[:, 1:end-1] == refdf - df[!, "b"] .= [1, 2, 3] + df[!, "b"] = [1, 2, 3] @test Matrix(df) == [1.5 4.5 7.5 10.5 13.5 1.0 1.0 2.5 5.5 8.5 11.5 14.5 1.0 2.0 3.5 6.5 9.5 12.5 15.5 1.0 3.0] @test names(df)[end] == "b" @test df[:, 1:end-2] == refdf cdf = copy(df) - @test_throws DimensionMismatch df[!, "c"] .= ones(1, 3) + @test_throws ArgumentError df[!, "c"] .= ones(1, 3) + @test df == cdf + @test_throws ArgumentError df[!, "d"] .= 1 @test df == cdf - @test_throws DimensionMismatch df[!, "x"] .= ones(4) + @test_throws ArgumentError df[!, "x"] .= ones(4) @test df == cdf - @test_throws ArgumentError df[!, 10] .= ones(3) + @test_throws BoundsError df[!, 10] .= ones(3) + @test_throws ArgumentError df[!, 10] = ones(3) @test df == cdf dfv = @view df[1:2, 2:end] - @test_throws ArgumentError dfv[!, 10] .= ones(3) + @test_throws BoundsError dfv[!, 10] .= ones(3) + @test_throws ArgumentError dfv[!, 10] = ones(3) @test_throws ArgumentError dfv[!, "z"] .= ones(3) @test df == cdf dfr = df[1, 3:end] @@ -823,105 +862,215 @@ end @test df == cdf df = DataFrame() - @test_throws DimensionMismatch df[!, "a"] .= sin.(1:3) - df[!, "b"] .= sin.(1) - df[!, "c"] .= sin(1) .+ 1 - @test df == DataFrame(b=Float64[], c=Float64[]) + df[!, "a"] = sin.(1:3) + df[!, "b"] = sin.(1) + df[!, "c"] = sin(1) .+ 1 + @test df != DataFrame(b=Float64[], c=Float64[]) + @test size(df) == (3, 3) end @testset "empty data frame corner case" begin df = DataFrame() - @test_throws ArgumentError df[!, 1] .= 1 - @test_throws ArgumentError df[!, 2] .= 1 + @test_throws BoundsError df[!, 1] .= 1 + @test_throws ArgumentError df[!, 1] = 1 + @test_throws BoundsError df[!, 2] .= 1 + @test_throws ArgumentError df[!, 2] = 1 @test_throws ArgumentError df[!, [:a, :b]] .= [1] @test_throws ArgumentError df[!, [:a, :b]] .= 1 - @test_throws DimensionMismatch df[!, :a] .= [1 2] - @test_throws DimensionMismatch df[!, :a] .= [1, 2] - @test_throws DimensionMismatch df[!, :a] .= sin.(1) .+ [1, 2] + @test_throws ArgumentError df[!, :a] .= [1 2] + @test_throws ArgumentError df[!, :a] .= [1, 2] + @test_throws ArgumentError df[!, :a] .= sin.(1) .+ [1, 2] @test_throws ArgumentError df[!, ["a", "b"]] .= [1] @test_throws ArgumentError df[!, ["a", "b"]] .= 1 - @test_throws DimensionMismatch df[!, "a"] .= [1 2] - @test_throws DimensionMismatch df[!, "a"] .= [1, 2] - @test_throws DimensionMismatch df[!, "a"] .= sin.(1) .+ [1, 2] + @test_throws ArgumentError df[!, "a"] .= [1 2] + @test_throws ArgumentError df[!, "a"] .= [1, 2] + @test_throws ArgumentError df[!, "a"] .= sin.(1) .+ [1, 2] + + @testset "$rhs" for rhs in [1, [1], Int[], "abc", ["abc"]] + n = rhs isa AbstractVector ? length(rhs) : 0 - for rhs in [1, [1], Int[], "abc", ["abc"]] df = DataFrame() - df[!, :a] .= rhs - @test size(df) == (0, 1) + @test_throws ArgumentError df.a .= rhs + df.a = rhs + @test size(df) == (n, 1) @test eltype(df[!, 1]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) df = DataFrame() - df[!, :a] .= length.(rhs) - @test size(df) == (0, 1) + @test_throws ArgumentError df.a .= length.(rhs) + df.a = length.(rhs) + @test size(df) == (n, 1) @test eltype(df[!, 1]) == Int df = DataFrame() - df[!, :a] .= length.(rhs) .+ 1 - @test size(df) == (0, 1) + @test_throws ArgumentError df[!, :a] .= rhs + df[!, :a] = rhs + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + + df = DataFrame() + df[!, :a] = length.(rhs) .+ 1 + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == Int + + df = DataFrame() + @test_throws ArgumentError df[!, :a] .= length.(rhs) + df[!, :a] = length.(rhs) + @test size(df) == (n, 1) @test eltype(df[!, 1]) == Int df = DataFrame() - @. df[!, :a] = length(rhs) + 1 + @test_throws ArgumentError df[!, :a] .= length.(rhs) .+ 1 + df[!, :a] = length.(rhs) .+ 1 + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == Int + + df = DataFrame() + @test_throws ArgumentError df[!, :a] .= length.(rhs) + df[!, :a] = length.(rhs) + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == Int + + df = DataFrame() + @test_throws ArgumentError df[!, :a] .= length.(rhs) + df[!, :a] = length.(rhs) + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == Int + + df = DataFrame() + @test_throws ArgumentError df[!, :a] .= length.(rhs) .+ 1 + df[!, :a] = length.(rhs) .+ 1 + @test size(df) == (n, 1) + @test eltype(df[!, 1]) == Int + + df = DataFrame() + @test_throws ArgumentError @. df[!, :a] = length(rhs) + 1 + df[!, :a] = length(rhs) + 1 @test size(df) == (0, 1) @test eltype(df[!, 1]) == Int + df = DataFrame(x=Union{Int,String}[]) + df.x .= rhs + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df.a = rhs + else + df.a = rhs + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + end + df = DataFrame(x=Int[]) - df[!, :a] .= rhs - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + df[!, :x] .= length.(rhs) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, :a] = length.(rhs) + else + df[!, :a] = length.(rhs) + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == Int + end df = DataFrame(x=Int[]) - df[!, :a] .= length.(rhs) - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == Int + if rhs isa AbstractVector || rhs isa Int + df[!, :x] .= rhs + end + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, :a] = rhs + else + df[!, :a] = rhs + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + end df = DataFrame(x=Int[]) - df[!, :a] .= length.(rhs) .+ 1 - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == Int + df[!, :x] .= length.(rhs) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, :a] = length.(rhs) + else + df[!, :a] = length.(rhs) + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == Int + end + + df = DataFrame(x=Int[]) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, :a] = length.(rhs) .+ 1 + else + df[!, :a] = length.(rhs) .+ 1 + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == Int + end df = DataFrame(x=Int[]) - @. df[!, :a] = length(rhs) + 1 + @test_throws ArgumentError @. df[!, :a] = length(rhs) + 1 + df[!, :a] = length(rhs) + 1 @test size(df) == (0, 2) @test eltype(df[!, 2]) == Int df = DataFrame() - df[!, "a"] .= rhs - @test size(df) == (0, 1) + @test_throws ArgumentError df[!, "a"] .= rhs + df[!, "a"] = rhs + @test size(df) == (n, 1) @test eltype(df[!, 1]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) df = DataFrame() - df[!, "a"] .= length.(rhs) - @test size(df) == (0, 1) + @test_throws ArgumentError df[!, "a"] .= length.(rhs) + @test size(df) == (0, 0) + df[!, "a"] = length.(rhs) + @test size(df) == (n, 1) @test eltype(df[!, 1]) == Int df = DataFrame() - df[!, "a"] .= length.(rhs) .+ 1 - @test size(df) == (0, 1) + @test_throws ArgumentError df[!, "a"] .= length.(rhs) .+ 1 + @test size(df) == (0, 0) + df[!, "a"] = length.(rhs) .+ 1 + @test size(df) == (n, 1) @test eltype(df[!, 1]) == Int df = DataFrame() - @. df[!, "a"] = length(rhs) + 1 + @test_throws ArgumentError @. df[!, "a"] = length(rhs) + 1 + @test size(df) == (0, 0) + df[!, "a"] = length(rhs) + 1 @test size(df) == (0, 1) @test eltype(df[!, 1]) == Int + @test eltype(df.a) == Int + @test_throws ArgumentError eltype(df.b) df = DataFrame(x=Int[]) - df[!, "a"] .= rhs - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + @test_throws ArgumentError df[!, "a"] .= rhs + @test size(df) == (0, 1) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, "a"] = rhs + else + df[!, "a"] = rhs + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == (rhs isa AbstractVector ? eltype(rhs) : typeof(rhs)) + end df = DataFrame(x=Int[]) - df[!, "a"] .= length.(rhs) - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == Int + @test_throws ArgumentError df[!, "a"] .= length.(rhs) + @test size(df) == (0, 1) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, "a"] = length.(rhs) + else + df[!, "a"] = length.(rhs) + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == Int + end df = DataFrame(x=Int[]) - df[!, "a"] .= length.(rhs) .+ 1 - @test size(df) == (0, 2) - @test eltype(df[!, 2]) == Int + @test_throws ArgumentError df[!, "a"] .= length.(rhs) .+ 1 + @test size(df) == (0, 1) + if rhs isa AbstractVector && length(rhs) != 0 + @test_throws DimensionMismatch df[!, "a"] = length.(rhs) .+ 1 + else + df[!, "a"] = length.(rhs) .+ 1 + @test size(df) == (n, 2) + @test eltype(df[!, 2]) == Int + end df = DataFrame(x=Int[]) - @. df[!, "a"] = length(rhs) + 1 + @test_throws ArgumentError @. df[!, "a"] = length(rhs) + 1 + @test size(df) == (0, 1) + df[!, "a"] = length(rhs) + 1 @test size(df) == (0, 2) @test eltype(df[!, 2]) == Int end @@ -937,36 +1086,51 @@ end @test_throws DimensionMismatch df .= ones(1, 2, 1) df = DataFrame(a=[]) - df[!, :b] .= sin.(1) + @test_throws ArgumentError df[!, :b] .= sin.(1) + df[!, :b] = sin.(1) @test eltype(df.b) == Float64 df[!, :b] .= [1] + @test eltype(df.b) == Float64 + @test_throws DimensionMismatch df[!, :b] = [1] + df[!, :b] = Int[] @test eltype(df.b) == Int df[!, :b] .= 'a' + @test eltype(df.b) == Int + df[!, :b] = 'a' @test eltype(df.b) == Char @test names(df) == ["a", "b"] c = categorical(["a", "b", "c"]) df = DataFrame() - @test_throws DimensionMismatch df[!, :a] .= c + @test_throws ArgumentError df[!, :a] .= c - df[!, :b] .= c[1] + @test_throws ArgumentError df[!, :b] .= c[1] + df[!, :b] = c[1] @test nrow(df) == 0 @test df.b isa CategoricalVector{String} df = DataFrame(a=[]) - df[!, "b"] .= sin.(1) + @test_throws ArgumentError df[!, "b"] .= sin.(1) + df[!, "b"] = sin.(1) + @test eltype(df."b") == Float64 + df[!, "b"] = sin.(1) @test eltype(df."b") == Float64 df[!, "b"] .= [1] + @test eltype(df."b") == Float64 + df[!, "b"] = Int[] @test eltype(df."b") == Int df[!, "b"] .= 'a' + @test eltype(df."b") == Int + df[!, "b"] = 'a' @test eltype(df."b") == Char @test names(df) == ["a", "b"] c = categorical(["a", "b", "c"]) df = DataFrame() - @test_throws DimensionMismatch df[!, "a"] .= c + @test_throws ArgumentError df[!, "a"] .= c - df[!, "b"] .= c[1] + @test_throws ArgumentError df[!, "b"] .= c[1] + df[!, "b"] = c[1] @test nrow(df) == 0 @test df."b" isa CategoricalVector{String} end @@ -977,29 +1141,29 @@ end categorical(["1", "2", "3"]), categorical(["1", "2", missing]), categorical([missing, "1", "2"])] df = copy(refdf) - df[!, :c1] .= v + df[:, :c1] = v @test df.c1 ≅ v @test df.c1 !== v @test df.c1 isa CategoricalVector @test levels(df.c1) == levels(v) @test levels(df.c1) !== levels(v) - df[!, :c2] .= v[2] + df[!, :c2] = v[2] @test df.c2 == fill(v[2], 3) @test df.c2 isa CategoricalVector @test levels(df.c2) == levels(v) - df[!, :c3] .= (x->x).(v) + df[!, :c3] = (x -> x).(v) @test df.c3 ≅ v @test df.c3 !== v @test df.c3 isa CategoricalVector @test levels(df.c3) == levels(v) @test levels(df.c3) !== levels(v) - df[!, :c4] .= identity.(v) + df[!, :c4] = identity.(v) @test df.c4 ≅ v @test df.c4 !== v @test df.c4 isa CategoricalVector @test levels(df.c4) == levels(v) @test levels(df.c4) !== levels(v) - df[!, :c5] .= (x->v[2]).(v) + df[!, :c5] = (x -> v[2]).(v) @test unique(df.c5) == [unwrap(v[2])] @test df.c5 isa CategoricalVector @test levels(df.c5) == levels(v) @@ -1061,7 +1225,7 @@ end @test X == DataFrame([1 2; 3 4], :auto) X = DataFrame([1 2; 3 4], :auto) - foreach(i -> X[!, i] .= nothing, axes(X, 2)) + foreach(i -> X[!, i] = nothing, axes(X, 2)) @test (X .== nothing) == DataFrame(trues(2, 2), :auto) end @@ -1096,7 +1260,7 @@ end df1 = DataFrame(rand(100, 100), :auto) df2 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] end df3 = copy(df2) df1 .= df2 @@ -1108,7 +1272,7 @@ end df1 = DataFrame(rand(100, 100), :auto) df2 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] end df3 = copy(df2) df1 .= view(df2, :, :) @@ -1120,7 +1284,7 @@ end df1 = DataFrame(rand(100, 100), :auto) df2 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] end df3 = copy(df2) view(df1, :, :) .= df2 @@ -1133,8 +1297,8 @@ end df2 = copy(df1) df3 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] - df3[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] + @alias df3[!, rand(1:100)] = df1[!, i] end df6 = copy(df2) df7 = copy(df3) @@ -1151,8 +1315,8 @@ end df2 = copy(df1) df3 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] - df3[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] + @alias df3[!, rand(1:100)] = df1[!, i] end df6 = copy(df2) df7 = copy(df3) @@ -1169,8 +1333,8 @@ end df2 = copy(df1) df3 = copy(df1) for i in 1:100 - df2[!, rand(1:100)] = df1[!, i] - df3[!, rand(1:100)] = df1[!, i] + @alias df2[!, rand(1:100)] = df1[!, i] + @alias df3[!, rand(1:100)] = df1[!, i] end df6 = copy(df2) df7 = copy(df3) @@ -1396,10 +1560,10 @@ end df = copy(refdf) v1 = df[!, 1] v1′ = df[:, 1] - df[!, 1] .= 100.0 + df[!, 1] = 100.0 @test df.x1 == [100.0, 100.0, 100.0] @test v1 == v1′ - df[!, 1] .= 'd' + df[!, 1] = 'd' @test df.x1 == ['d', 'd', 'd'] @test v1 == v1′ @test_throws DimensionMismatch df[!, 1] .= [1 2 3] @@ -1409,10 +1573,10 @@ end df = copy(refdf) v1 = df[!, 1] v1′ = df[:, 1] - df[!, :x1] .= 100.0 + df[!, :x1] = 100.0 @test df.x1 == [100.0, 100.0, 100.0] @test v1 == v1′ - df[!, :x1] .= 'd' + df[!, :x1] = 'd' @test df.x1 == ['d', 'd', 'd'] @test v1 == v1′ @test_throws DimensionMismatch df[!, :x1] .= [1 2 3] @@ -1420,39 +1584,39 @@ end @test v1 == v1′ df = copy(refdf) - df[!, :newcol] .= 100.0 + df[!, :newcol] = 100.0 @test df.newcol == [100.0, 100.0, 100.0] @test df[:, 1:end-1] == refdf df = copy(refdf) - df[!, "newcol"] .= 100.0 + df[!, "newcol"] = 100.0 @test df.newcol == [100.0, 100.0, 100.0] @test df[:, 1:end-1] == refdf df = copy(refdf) - df[!, :newcol] .= 'd' + df[!, :newcol] = 'd' @test df.newcol == ['d', 'd', 'd'] @test df[:, 1:end-1] == refdf df = copy(refdf) - df[!, "newcol"] .= 'd' + df[!, "newcol"] = 'd' @test df.newcol == ['d', 'd', 'd'] @test df[:, 1:end-1] == refdf df = copy(refdf) - @test_throws DimensionMismatch df[!, :newcol] .= [1 2 3] + @test_throws ArgumentError df[!, :newcol] = [1 2 3] @test df == refdf df = copy(refdf) - @test_throws DimensionMismatch df[!, "newcol"] .= [1 2 3] + @test_throws ArgumentError df[!, "newcol"] = [1 2 3] @test df == refdf df = copy(refdf) - @test_throws ArgumentError df[!, 10] .= 'a' + @test_throws BoundsError df[!, 10] .= 'a' @test df == refdf - @test_throws ArgumentError df[!, 10] .= [1, 2, 3] + @test_throws BoundsError df[!, 10] .= [1, 2, 3] @test df == refdf - @test_throws ArgumentError df[!, 10] .= [1 2 3] + @test_throws BoundsError df[!, 10] .= [1 2 3] @test df == refdf df = copy(refdf) @@ -1461,47 +1625,43 @@ end 'a' 'a' 8.5 11.5 14.5 'a' 'a' 9.5 12.5 15.5] + df = copy(refdf) + df[!, 2:3] = 'b' + @test Matrix(df) == [1.5 'b' 'b' 10.5 13.5 + 2.5 'b' 'b' 11.5 14.5 + 3.5 'b' 'b' 12.5 15.5] + df = copy(refdf) v1 = df[!, 1] - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - df.x1 .= 'd' - @test df.x1 == ['d', 'd', 'd'] - @test eltype(df.x1) === Char - @test_throws MethodError df[:, 1] .= "d" - @test_throws DimensionMismatch df[:, 1] .= [1 2 3] - @test v1 == [1.5, 2.5, 3.5] - else - df.x1 .= 'd' - @test v1 == [100.0, 100.0, 100.0] - @test_throws MethodError df[:, 1] .= "d" - @test v1 == [100.0, 100.0, 100.0] - @test_throws DimensionMismatch df[:, 1] .= [1 2 3] - @test v1 == [100.0, 100.0, 100.0] - end + df.x1 .= 'd' + @test v1 == [100.0, 100.0, 100.0] + @test_throws MethodError df[:, 1] .= "d" + @test v1 == [100.0, 100.0, 100.0] + @test_throws DimensionMismatch df[:, 1] .= [1 2 3] + @test v1 == [100.0, 100.0, 100.0] - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - df = DataFrame(a=1:4, b=1, c=2) - df.a .= 'a':'d' - @test df == DataFrame(a='a':'d', b=1, c=2) - dfv = view(df, 2:3, 2:3) - x = df.b - dfv.b .= 0 - @test df.b == [1, 0, 0, 1] - @test x == [1, 1, 1, 1] - else - df = DataFrame(a=1:4, b=1, c=2) - df.a .= 'a':'d' - @test df == DataFrame(a=97:100, b=1, c=2) - dfv = view(df, 2:3, 2:3) - x = df.b - dfv.b .= 0 - @test df.b == [1, 0, 0, 1] - @test x === df.b - end + df.x1 = 'd' + @test df.x1 == ['d', 'd', 'd'] + + df = DataFrame(a=1:4, b=1, c=2) + df.a .= 'a':'d' + @test df == DataFrame(a=97:100, b=1, c=2) + df.a = 'a':'d' + @test df == DataFrame(a='a':'d', b=1, c=2) + dfv = view(df, 2:3, 2:3) + x = df.b + dfv.b .= 0 + @test df.b == [1, 0, 0, 1] + @test x == [1, 0, 0, 1] + @test x === df.b + dfv.b = 2 + @test df.b == [1, 2, 2, 1] + @test x == [1, 0, 0, 1] + @test x !== df.b df = copy(refdf) if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - df.newcol .= 'd' + df.newcol = 'd' @test df == [refdf DataFrame(newcol=fill('d', 3))] else @test_throws ArgumentError df.newcol .= 'd' @@ -1618,16 +1778,19 @@ end @test eltype(parent(df).x1) == Float64 df = view(copy(refdf), :, :) - df[!, :newcol] .= 100.0 + df[!, :newcol] = 100.0 @test parent(df).newcol == [100, 100, 100] @test eltype(parent(df).newcol) == Union{Float64, Missing} df = view(copy(refdf), :, :) - @test_throws ArgumentError df[!, 10] .= 'a' + @test_throws BoundsError df[!, 10] .= 'a' + @test_throws ArgumentError df[!, 10] = 'a' @test df == refdf - @test_throws ArgumentError df[!, 10] .= [1, 2, 3] + @test_throws BoundsError df[!, 10] .= [1, 2, 3] + @test_throws ArgumentError df[!, 10] = [1, 2, 3] @test df == refdf - @test_throws ArgumentError df[!, 10] .= [1 2 3] + @test_throws BoundsError df[!, 10] .= [1 2 3] + @test_throws ArgumentError df[!, 10] = [1 2 3] @test df == refdf df = view(copy(refdf), :, :) @@ -1637,26 +1800,17 @@ end df = view(copy(refdf), :, :) v1 = df[!, 1] - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - df.x1 .= 'd' - @test df.x1 == ['d', 'd', 'd'] - @test eltype(df.x1) === Any - df[:, 1] .= "d" - @test df.x1 == ["d", "d", "d"] - @test_throws DimensionMismatch df[:, 1] .= [1 2 3] - @test v1 == [1.5, 2.5, 3.5] - else - df.x1 .= 'd' - @test v1 == [100.0, 100.0, 100.0] - @test_throws MethodError df[:, 1] .= "d" - @test v1 == [100.0, 100.0, 100.0] - @test_throws DimensionMismatch df[:, 1] .= [1 2 3] - @test v1 == [100.0, 100.0, 100.0] - end + df.x1 .= 'd' + @test df.x1 == [100.0, 100.0, 100.0] + @test eltype(df.x1) === Float64 + df[!, 1] = "d" + @test df.x1 == ["d", "d", "d"] + @test_throws DimensionMismatch df[:, 1] .= [1 2 3] + @test v1 == [100.0, 100.0, 100.0] df = view(copy(refdf), :, :) if VERSION >= v"1.7" - df.newcol .= 'd' + df.newcol = 'd' @test df.newcol == fill('d', 3) else @test_throws ArgumentError df.newcol .= 'd' @@ -1690,13 +1844,13 @@ end @test_throws MethodError dfr."a" .= ["a", "b"] end -@testset "make sure that : is in place and ! allocates" begin +@testset "make sure that .= is in place and = replaces column" begin df = DataFrame(a=[1, 2, 3]) a = df.a df[:, :a] .+= 1 @test a == [2, 3, 4] @test df.a === a - df[!, :a] .+= 1 + df[!, :a] = df[!, :a] .+ 1 @test a == [2, 3, 4] @test df.a == [3, 4, 5] @test df.a !== a @@ -1706,7 +1860,7 @@ end df[:, "a"] .+= 1 @test a == [2, 3, 4] @test df.a === a - df[!, "a"] .+= 1 + df[!, "a"] = df[!, "a"] .+ 1 @test a == [2, 3, 4] @test df.a == [3, 4, 5] @test df.a !== a @@ -1875,7 +2029,7 @@ end @testset "broadcasting of df[:, col] = value" begin df = DataFrame(ones(3, 4), :auto) z = ["a", "b", "c"] - df[:, :z] .= z + df[:, :z] = z @test df.z == z @test df.z !== z @test_throws ArgumentError df[:, 6] .= z @@ -1887,6 +2041,10 @@ end @test df.z == fill("abc", 3) @test_throws ArgumentError df[:, 6] .= z @test_throws MethodError df[:, 1] .= z + dz = df.z + df[:, "z"] .= z + @test dz === df.z + @test dz == df[:, "z"] df = DataFrame(ones(3, 4), :auto) z = fill("abc", 1, 1, 2) @@ -1894,14 +2052,22 @@ end df = DataFrame(ones(3, 4), :auto) z = ["a", "b", "c"] - df[:, "z"] .= z + df[:, "z"] = z @test df.z == z @test df.z !== z + dz = df.z + df[:, "z"] .= z + @test dz === df.z + @test dz == df[:, "z"] df = DataFrame(ones(3, 4), :auto) z = "abc" df[:, "z"] .= z @test df.z == fill("abc", 3) + dz = df.z + df[:, "z"] .= z + @test dz === df.z + @test dz == df[:, "z"] df = DataFrame(ones(3, 4), :auto) z = fill("abc", 1, 1, 2) @@ -1910,47 +2076,39 @@ end @testset "broadcasting of getproperty" begin df = DataFrame(a=1:4) - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - df.b .= 1 - x = df.b - df.c .= 4:-1:1 - df.a .= 'a':'d' - @test df.a isa Vector{Char} - @test df == DataFrame(a='a':'d', b=1, c=4:-1:1) - - # in views also column replacement is performed - dfv = view(df, 2:3, 2:3) - dfv.b .= 0 - @test x == [1, 1, 1, 1] - @test df.b !== x - @test df == DataFrame(a='a':'d', b=[1, 0, 0, 1], c=4:-1:1) - dfv.c .= ["p", "q"] - @test df == DataFrame(a='a':'d', b=[1, 0, 0, 1], c=[4, "p", "q", 1]) - else - # Julia older than 1.7 - df[!, :b] .= 1 - x = df.b - df[!, :c] .= 4:-1:1 - df.a .= 'a':'d' - dfv = view(df, 2:3, 2:3) - dfv.b .= 0 - @test x == [1, 0, 0, 1] - @test df.b === x - @test df == DataFrame(a=97:100, b=[1, 0, 0, 1], c=4:-1:1) - @test_throws MethodError dfv.c .= ["p", "q"] - @test df == DataFrame(a=97:100, b=[1, 0, 0, 1], c=[4, 3, 2, 1]) - end + @test_throws ArgumentError df.b .= 1 + df.b = 1 + x = df.b + @test_throws ArgumentError df.c .= 4:-1:1 + df.c = 4:-1:1 + df.a .= 'a':'d' + @test df.a isa Vector{Int} + @test df == DataFrame(a=97:100, b=1, c=4:-1:1) + df.a = 'a':'d' + @test df.a isa Vector{Char} + @test df == DataFrame(a='a':'d', b=1, c=4:-1:1) + + # in views also column replacement is performed + dfv = view(df, 2:3, 2:3) + dfv.b .= 0 + @test x == [1, 0, 0, 1] + @test df.b === x + dfv.b = 0 + @test df.b !== x + @test df == DataFrame(a='a':'d', b=[1, 0, 0, 1], c=4:-1:1) + @test_throws MethodError dfv.c .= ["p", "q"] + dfv.c = ["p", "q"] + @test df == DataFrame(a='a':'d', b=[1, 0, 0, 1], c=[4, "p", "q", 1]) end @testset "dotgetproperty on SubDataFrame" begin df = DataFrame(a=1:3, b=4:6) dfv = @view df[[3, 1], :] - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - dfv.c .= [1, 2] - @test df ≅ DataFrame(a=1:3, b=4:6, c=[2, missing, 1]) - else - @test_throws ArgumentError dfv.c .= [1, 2] - end + @test_throws ArgumentError dfv.c .= [1, 2] + dfv.c = [1, 2] + @test df ≅ DataFrame(a=1:3, b=4:6, c=[2, missing, 1]) + dfv.a .= [4, 5] + @test df ≅ DataFrame(a=[5,2,4], b=4:6, c=[2, missing, 1]) df = DataFrame(a=1:3, b=4:6) dfv = @view df[[3, 1], 1:2] diff --git a/test/constructors.jl b/test/constructors.jl index 391415e572..1ac47b4212 100644 --- a/test/constructors.jl +++ b/test/constructors.jl @@ -324,7 +324,7 @@ end df[!, :D] = [4, 5, missing] push!(answer, Vector{Union{Int, Missing}}) @test map(typeof, eachcol(df)) == answer - df[!, :E] .= 'c' + df[!, :E] = 'c' push!(answer, Vector{Char}) @test map(typeof, eachcol(df)) == answer end diff --git a/test/dataframe.jl b/test/dataframe.jl index 27a5108fb3..37993a6361 100644 --- a/test/dataframe.jl +++ b/test/dataframe.jl @@ -2,7 +2,7 @@ module TestDataFrame using Dates, DataFrames, Statistics, Random, Test, Logging, DataStructures, CategoricalArrays -using DataFrames: _columns, index +using DataFrames: _columns, index, @alias using OffsetArrays: OffsetArray const ≅ = isequal const ≇ = !isequal @@ -148,7 +148,7 @@ end @test DataFrames._columns(dfdc) !== DataFrames._columns(df) df[1, :a] = 4 - df[1, :b][!, :e] .= 5 + df[1, :b][!, :e] = 5 @test names(rename(df, [:f, :g])) == ["f", "g"] @test names(rename(df, [:f, :f], makeunique=true)) == ["f", "f_1"] @@ -625,7 +625,7 @@ end df = DataFrame() df.a = [1, 2, 3] - df.b = df.a + @alias df.b = df.a dfc = copy(df) with_logger(sl) do @test_throws AssertionError append!(df, dfc) @@ -635,7 +635,7 @@ end df = DataFrame() df.a = [1, 2, 3, 4] - df.b = df.a + @alias df.b = df.a df.c = [1, 2, 3, 4] dfc = copy(df) with_logger(sl) do @@ -696,7 +696,7 @@ end df = DataFrame() df.a = [1, 2, 3] - df.b = df.a + @alias df.b = df.a dfc = copy(df) with_logger(sl) do @test_throws AssertionError prepend!(df, dfc) @@ -706,7 +706,7 @@ end df = DataFrame() df.a = [1, 2, 3, 4] - df.b = df.a + @alias df.b = df.a df.c = [1, 2, 3, 4] dfc = copy(df) with_logger(sl) do @@ -832,7 +832,7 @@ end df1 = DataFrame() df1.x = 1:3 - df1.y = df1.x + @alias df1.y = df1.x df2 = DataFrame(x=1:3, y=4:6) with_logger(sl) do @test_throws AssertionError append!(df1, df2, cols=cols, promote=promote) @@ -946,7 +946,7 @@ end df1 = DataFrame() df1.x = 1:3 - df1.y = df1.x + @alias df1.y = df1.x df2 = DataFrame(x=1:3, y=4:6) with_logger(sl) do @test_throws AssertionError prepend!(df1, df2, cols=cols, promote=promote) @@ -1610,14 +1610,10 @@ end @test x == 1:10 df.y .= 1 @test df.y == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - @test y == 1.0:10.0 - else - @test df.y === y - end - df.z = z + @test df.y === y + @alias df.z = z @test df.z === z - df[!, :zz] .= 1 + df[!, :zz] = 1 @test df.zz == df.y end @@ -1922,7 +1918,7 @@ end @test reverse(df, 3) == df[[1; 2; end:-1:3], :] df = DataFrame(a=1:5) - df.b = df.a + @alias df.b = df.a @test reverse(df) == DataFrame(a=5:-1:1, b=5:-1:1) end @@ -1938,14 +1934,14 @@ end @test reverse!(df, 3) == cdf[[1; 2; end:-1:3], :] df = DataFrame(a=1:5) - df.b = df.a + @alias df.b = df.a df.c = 11:15 @test reverse!(df) == DataFrame(a=5:-1:1, b=5:-1:1, c=15:-1:11) x = collect(1:6) df = DataFrame() - df.a = view(x, 1:5) - df.b = view(x, 2:6) + @alias df.a = view(x, 1:5) + @alias df.b = view(x, 2:6) @test reverse(df) == DataFrame(a=5:-1:1, b=6:-1:2) # incorrect result due to aliasing @test reverse!(df) != DataFrame(a=5:-1:1, b=6:-1:2) @@ -1970,13 +1966,13 @@ end @test reverse(view(df, 2:5, 2:3), 2, 3) == DataFrame(b=[9, 7, 8, 6], c=[12, 14, 13, 15]) df = DataFrame(a=1:5) - df.b = df.a + @alias df.b = df.a @test reverse!(view(df, 2:4, 1:2)) == DataFrame(a=4:-1:2, b=4:-1:2) x = collect(1:6) df = DataFrame() - df.a = view(x, 1:5) - df.b = view(x, 2:6) + @alias df.a = view(x, 1:5) + @alias df.b = view(x, 2:6) dfv = view(df, 2:4, :) @test reverse(dfv) == DataFrame(a=4:-1:2, b=5:-1:3) # incorrect result due to aliasing @@ -1986,7 +1982,7 @@ end @testset "permute!, invpermute!" begin df = DataFrame(a=1:5, b=6:10, c=11:15) - df.d = df.a + @alias df.d = df.a dfc = copy(df) @test permute!(df, [5, 3, 1, 2, 4]) === df @test df == dfc[[5, 3, 1, 2, 4], :] @@ -1994,7 +1990,7 @@ end @test df == dfc df = DataFrame(a=1:5, b=6:10, c=11:15) - df.d = df.a + @alias df.d = df.a dfc = copy(df) @test permute!(df, [5, 3, 1, 2, 4]) === df @test df == dfc[[5, 3, 1, 2, 4], :] @@ -2067,7 +2063,7 @@ end @testset "shuffle, shuffle!" begin refdf = DataFrame(a=1:5, b=11:15) - refdf.c = refdf.a + @alias refdf.c = refdf.a for df in (refdf, view(refdf, 2:5, [2, 1])) x = randperm(MersenneTwister(1234), nrow(df)) mt = MersenneTwister(1234) diff --git a/test/grouping.jl b/test/grouping.jl index d26f09cb22..a6336a96d2 100644 --- a/test/grouping.jl +++ b/test/grouping.jl @@ -4171,7 +4171,7 @@ end # eachindex on DataFrame @test combine(df, eachindex) == DataFrame(eachindex=1:6) @test isequal_coltyped(combine(DataFrame(), eachindex), - DataFrame(eachindex=Int[])) + DataFrame(eachindex=Base.OneTo(0), copycols = false)) # Disallowed operations @test_throws ArgumentError groupindices(df) @@ -4248,14 +4248,16 @@ end DataFrame(x = df.x, id = df.id, eachindex = 1:6) df = view(df, [], :) df2 = combine(df, eachindex) - @test isequal_coltyped(df2, DataFrame(eachindex = Int[])) + @test isequal_coltyped(df2, DataFrame(eachindex = Base.OneTo(0), copycols = false)) @test isequal_coltyped(df2, combine(eachindex, df)) - @test isequal_coltyped(df2, rename(combine(df, eachindex => :a), :a => :eachindex)) + @test isequal_coltyped(df2, rename!(combine(df, eachindex => :a), :a => :eachindex)) + @test isequal_coltyped(copy(df2), rename(combine(df, eachindex => :a), :a => :eachindex)) df2 = transform(df, eachindex) - @test isequal_coltyped(df2, DataFrame(x = Int[], id = Int[], eachindex = Int[])) + @test isequal_coltyped(df2, DataFrame(x = Int[], id = Int[], eachindex = Base.OneTo(0), copycols = false)) @test isequal_coltyped(df2, transform(eachindex, df)) - @test isequal_coltyped(df2, rename(transform(df, eachindex => :a), :a => :eachindex)) + @test isequal_coltyped(df2, rename!(transform(df, eachindex => :a), :a => :eachindex)) + @test isequal_coltyped(copy(df2), rename(transform(df, eachindex => :a), :a => :eachindex)) end @testset "fillfirst! correctness tests" begin diff --git a/test/indexing.jl b/test/indexing.jl index 867aa42292..755ffe3d79 100644 --- a/test/indexing.jl +++ b/test/indexing.jl @@ -582,7 +582,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 100] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 1000] @@ -592,7 +592,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @@ -649,7 +649,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 100] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 1000] @@ -659,7 +659,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @@ -728,7 +728,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 100] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 1000] @@ -738,7 +738,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @@ -811,7 +811,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 100] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5, 1000] @@ -821,7 +821,7 @@ end @test dfr isa DataFrameRow @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @test parent(dfr) === df2 - df2[!, :y] .= 100 + df2[!, :y] = 100 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] df2[!, "y"] .= 1000 @test Vector(dfr) == [2.5, 6.5, 10.5, 14.5] @@ -1107,8 +1107,8 @@ end df = DataFrame(a=1:3, b=4:6, c=7:9) df[!, 1] = ["a", "b", "c"] @test df == DataFrame(a=["a", "b", "c"], b=4:6, c=7:9) - @test_throws ArgumentError df[!, 1] = ["a", "b"] - @test_throws ArgumentError df[!, 1] = ["a"] + @test_throws DimensionMismatch df[!, 1] = ["a", "b"] + @test_throws DimensionMismatch df[!, 1] = ["a"] @test_throws ArgumentError df[!, 5] = ["a", "b", "c"] df[!, :a] = 'a':'c' @test df == DataFrame(a='a':'c', b=4:6, c=7:9) @@ -1127,8 +1127,8 @@ end df = DataFrame(a=1:3, b=4:6, c=7:9) df[!, "a"] = ["a", "b", "c"] @test df == DataFrame(a=["a", "b", "c"], b=4:6, c=7:9) - @test_throws ArgumentError df[!, "a"] = ["a", "b"] - @test_throws ArgumentError df[!, "a"] = ["a"] + @test_throws DimensionMismatch df[!, "a"] = ["a", "b"] + @test_throws DimensionMismatch df[!, "a"] = ["a"] df[!, "a"] = 'a':'c' @test df == DataFrame(a='a':'c', b=4:6, c=7:9) df."a" = ["aaa", "bbb", 1] @@ -1580,7 +1580,7 @@ end df[!, :] = DataFrame(reshape(1:12, 3, :), :auto) @test df == DataFrame(reshape(1:12, 3, :), :auto) @test_throws ArgumentError df[!, :] = DataFrame(fill(1, 3, 4), :auto)[:, [3, 2, 1]] - @test_throws ArgumentError df[!, :] = DataFrame(fill(1, 3, 4), :auto)[1:2, :] + @test_throws DimensionMismatch df[!, :] = DataFrame(fill(1, 3, 4), :auto)[1:2, :] df = DataFrame(fill("x", 3, 4), :auto) df[!, Not(4)] = DataFrame(reshape(1:12, 3, :), :auto)[:, 1:3] @@ -1850,17 +1850,27 @@ end @testset "setproperty! corner cases" begin df = DataFrame(a=1) - @test_throws ArgumentError df.a = 1 - @test_throws ArgumentError df."a" = 1 + df.a = 2 + @test df.a == [2] + df."a" = 3 + @test df.a == [3] + + df = DataFrame(a=1) + dfv = @view df[:, :] + @test_throws MethodError @alias dfv.a = [5] dfv.a = [5] @test df == DataFrame(a=5) @test eltype(df.a) === Int + @test_throws MethodError @alias dfv."a" = [6] dfv."a" = [6] @test df == DataFrame(a=6) @test eltype(df.a) === Int - @test_throws ArgumentError dfv.a = 1 - @test_throws ArgumentError dfv."a" = 1 + + dfv.a = 7 + @test df.a == [7] + dfv."a" = 8 + @test df.a == [8] end @testset "disallowed getindex and setindex! methods" begin diff --git a/test/indexing_begin_tests.jl b/test/indexing_begin_tests.jl index eb3c2b9380..a1b84f0cc5 100644 --- a/test/indexing_begin_tests.jl +++ b/test/indexing_begin_tests.jl @@ -19,9 +19,9 @@ df[end, end] = (103, 104) @test df[end, end] == (103, 104) - df[!, begin] .= [1, 2, 3] + df[!, begin] = [1, 2, 3] @test df[:, 1] == [1, 2, 3] - df[!, end] .= [11, 12, 13] + df[!, end] = [11, 12, 13] @test df[:, 4] == [11, 12, 13] @test df[begin:end, [begin, end]] == df[:, [1, 4]] diff --git a/test/indexing_offset.jl b/test/indexing_offset.jl index 36187ccc80..3e6c68837a 100644 --- a/test/indexing_offset.jl +++ b/test/indexing_offset.jl @@ -18,9 +18,9 @@ using Test, DataFrames, OffsetArrays DataFrames._columns(df)[1] = ov2 DataFrames._check_consistency(df) - @test_throws ArgumentError df.b = ov1 + @test_throws ArgumentError @alias df.b = ov1 @test_throws ArgumentError insertcols!(df, :b => ov1) - @test_throws DimensionMismatch df[!, :b] .= ov1 + @test_throws ArgumentError df.b = ov1 # this is consequence of the fact that OffsetArrays wrap AbstractRange in this case # Base.CanonicalIndexError is not available in Julia 1.7 or earlier diff --git a/test/insertion.jl b/test/insertion.jl index bc697abc02..b906e4716c 100644 --- a/test/insertion.jl +++ b/test/insertion.jl @@ -125,7 +125,7 @@ const ≅ = isequal df = DataFrame() df.a = [1, 2, 3] - df.b = df.a + @alias df.b = df.a dfc = copy(df) with_logger(sl) do @test_throws AssertionError push!(df, [1, 2]) @@ -151,7 +151,7 @@ const ≅ = isequal df = DataFrame() df.a = [1, 2, 3, 4] - df.b = df.a + @alias df.b = df.a df.c = [1, 2, 3, 4] dfc = copy(df) with_logger(sl) do @@ -307,7 +307,7 @@ end df = DataFrame() df.a = [1, 2, 3] - df.b = df.a + @alias df.b = df.a dfc = copy(df) with_logger(sl) do @test_throws AssertionError pushfirst!(df, [1, 2]) @@ -333,7 +333,7 @@ end df = DataFrame() df.a = [1, 2, 3, 4] - df.b = df.a + @alias df.b = df.a df.c = [1, 2, 3, 4] dfc = copy(df) with_logger(sl) do @@ -500,7 +500,7 @@ end df = DataFrame() df.a = [1, 2, 3] - df.b = df.a + @alias df.b = df.a dfc = copy(df) with_logger(sl) do @test_throws AssertionError insert!(df, 2, [1, 2]) @@ -526,7 +526,7 @@ end df = DataFrame() df.a = [1, 2, 3, 4] - df.b = df.a + @alias df.b = df.a df.c = [1, 2, 3, 4] dfc = copy(df) with_logger(sl) do @@ -1227,7 +1227,7 @@ end @test pushfirst!(df, df[3, :]) == DataFrame(a=[3; 1:3; 2], b=[4; 2:4; 3], c=[5; 3:5; 4]) @test insert!(df, 3, df[1, :]) == DataFrame(a=[3; 1; 3; 2:3; 2], b=[4; 2; 4; 3:4; 3], c=[5; 3; 5; 4:5; 4]) df = DataFrame(a=1:3, b=2:4) - df.c = df.a + @alias df.c = df.a @test_throws AssertionError push!(df, df[2, :]) @test_throws AssertionError pushfirst!(df, df[2, :]) @test_throws AssertionError insert!(df, 2, df[2, :]) @@ -1236,11 +1236,11 @@ end @testset "multicolumn aliasing" begin df = DataFrame(a1=1:3, b1=11:13) - df.a2 = df.a1 - df.a3 = df.a1 - df.b2 = df.b1 - df.b3 = df.b1 - df.a4 = df.a1 + @alias df.a2 = df.a1 + @alias df.a3 = df.a1 + @alias df.b2 = df.b1 + @alias df.b3 = df.b1 + @alias df.a4 = df.a1 refdf = copy(df) buf = IOBuffer() diff --git a/test/iteration.jl b/test/iteration.jl index 9edb91246e..678d581a02 100644 --- a/test/iteration.jl +++ b/test/iteration.jl @@ -73,7 +73,7 @@ end df = DataFrame(a=1) df = mapcols(x -> 2:2, df) @test df == DataFrame(a=2) - @test df.a isa Vector{Int} + @test df.a isa AbstractRange{Int} end @testset "mapcols!" begin @@ -126,7 +126,7 @@ end df[!, 1] = 51:56 @test df[1, :] == erd[1] @test copy(erv[1]) == (y3=33, y1=53, y4=43) - df[!, :z] .= 1 + df[!, :z] = 1 @test length(erd[1]) == 5 # the added column is reflected select!(df, Not([4, 5])) @test copy(erd[1]) == (y1 = 51, y2 = 21, y3 = 31) # the removed columns are reflected diff --git a/test/runtests.jl b/test/runtests.jl index 87c796c151..e6aa4b1f40 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -56,6 +56,7 @@ my_tests = ["utils.jl", "string.jl", "multithreading.jl", "metadata.jl", + "alias.jl", "deprecated.jl"] println("Running tests:") diff --git a/test/select.jl b/test/select.jl index c91df2ddc4..45a5af6e0c 100644 --- a/test/select.jl +++ b/test/select.jl @@ -925,7 +925,7 @@ end DataFrame(x1=Char[], a=Int[]) end @test_throws ArgumentError select(df, [] => (() -> [9]) => :a, :) - @test_throws ArgumentError select(df, :, [] => (() -> [9]) => :a) + @test_throws DimensionMismatch select(df, :, [] => (() -> [9]) => :a) @test transform(df, names(df) .=> (x -> 9) .=> names(df)) == repeat(DataFrame([9 9 9], :auto), nrow(df)) @test combine(df, names(df) .=> (x -> 9) .=> names(df)) == @@ -1011,7 +1011,7 @@ end @test df2.x4_last isa CategoricalVector{Int} end - @test_throws ArgumentError select(df, names(df) .=> first, [] => (() -> Int[]) => :x1) + @test_throws DimensionMismatch select(df, names(df) .=> first, [] => (() -> Int[]) => :x1) df2 = combine(df, names(df) .=> first, [] => (() -> Int[]) => :x1) @test size(df2) == (0, 5) @test df2.x1_first isa Vector{Int} @@ -1019,7 +1019,7 @@ end @test df2.x3_first isa Vector{Missing} @test df2.x4_first isa Vector{Missing} - @test_throws ArgumentError select(df, names(df) .=> last, [] => (() -> Int[]) => :x1) + @test_throws DimensionMismatch select(df, names(df) .=> last, [] => (() -> Int[]) => :x1) df2 = combine(df, names(df) .=> last, [] => (() -> Int[]) => :x1) @test size(df2) == (0, 5) @test df2.x1_last isa Vector{Int} @@ -1197,7 +1197,7 @@ end @testset "make sure select! is safe on error" begin a = [1] df = DataFrame() - df.a = a + @alias df.a = a @test_throws DomainError select!(df, :a => x -> sqrt(-1)) @test df.a === a @test propertynames(df) == [:a, ] @@ -1273,8 +1273,8 @@ end @test df2.y === df.y @test transform(df, names(df) .=> first .=> names(df)) == DataFrame(x=fill(1, 3), y=fill(4, 3)) - @test_throws ArgumentError transform(df, :x => x -> [first(x)], copycols=true) - @test_throws ArgumentError transform(df, :x => x -> [first(x)], copycols=false) + @test_throws DimensionMismatch transform(df, :x => x -> [first(x)], copycols=true) + @test_throws DimensionMismatch transform(df, :x => x -> [first(x)], copycols=false) dfv = view(df, [2, 1], [2, 1]) @test select(dfv, :x => first) == DataFrame(x_first=fill(2, 2)) @@ -1292,8 +1292,8 @@ end @test_throws ArgumentError transform(dfv, :x => first, copycols=false) @test transform(dfv, names(dfv) .=> first .=> names(dfv)) == DataFrame(y=fill(5, 2), x=fill(2, 2)) - @test_throws ArgumentError transform(df, :x => x -> [first(x)], copycols=true) - @test_throws ArgumentError transform(df, :x => x -> [first(x)], copycols=false) + @test_throws DimensionMismatch transform(df, :x => x -> [first(x)], copycols=true) + @test_throws DimensionMismatch transform(df, :x => x -> [first(x)], copycols=false) end @testset "select! and transform! AbstractDataFrame" begin @@ -1327,7 +1327,7 @@ end @test df == DataFrame(x=fill(1, 3), y=fill(4, 3)) df = DataFrame(x=1:3, y=4:6) - @test_throws ArgumentError transform!(df, :x => x -> [1]) + @test_throws DimensionMismatch transform!(df, :x => x -> [1]) @test df == DataFrame(x=1:3, y=4:6) dfv = view(df, [2, 1], [2, 1]) @@ -1387,7 +1387,7 @@ end @test transform(sdf -> sdf.b, df) == [df DataFrame(x1=3:4)] @test transform(sdf -> (b = 2sdf.b,), df) == DataFrame(a=1:2, b=[6, 8], c=5:6) @test transform(sdf -> (b = 1,), df) == DataFrame(a=[1, 2], b=[1, 1], c=[5, 6]) - @test_throws ArgumentError transform(sdf -> (b = [1],), df) + @test_throws DimensionMismatch transform(sdf -> (b = [1],), df) @test transform(sdf -> (b = [1, 5],), df) == DataFrame(a=[1, 2], b=[1, 5], c=[5, 6]) @test transform(sdf -> 1, df) == DataFrame(a=1:2, b=3:4, c=5:6, x1=1) @test transform(sdf -> fill([1]), df) == DataFrame(a=1:2, b=3:4, c=5:6, x1=[[1], [1]]) @@ -1397,8 +1397,8 @@ end for ret in (DataFrame(), NamedTuple(), zeros(0, 0), DataFrame(t=1)[1, 1:0]) @test transform(sdf -> ret, df) == df end - @test_throws ArgumentError transform(sdf -> DataFrame(a=10), df) - @test_throws ArgumentError transform(sdf -> zeros(1, 2), df) + @test_throws DimensionMismatch transform(sdf -> DataFrame(a=10), df) + @test_throws DimensionMismatch transform(sdf -> zeros(1, 2), df) @test transform(sdf -> DataFrame(a=[10, 11]), df) == DataFrame(a=[10, 11], b=3:4, c=5:6) @test transform(sdf -> [10 11; 12 13], df) == DataFrame(a=1:2, b=3:4, c=5:6, x1=[10, 12], x2=[11, 13]) @test transform(sdf -> DataFrame(a=10)[1, :], df) == DataFrame(a=[10, 10], b=3:4, c=5:6) @@ -1446,7 +1446,7 @@ end @test transform!(sdf -> sdf.b, copy(df)) == [df DataFrame(x1=3:4)] @test transform!(sdf -> (b = 2sdf.b,), copy(df)) == DataFrame(a=1:2, b=[6, 8], c=5:6) @test transform!(sdf -> (b = 1,), copy(df)) == DataFrame(a=[1, 2], b=[1, 1], c=[5, 6]) - @test_throws ArgumentError transform!(sdf -> (b = [1],), copy(df)) + @test_throws DimensionMismatch transform!(sdf -> (b = [1],), copy(df)) @test transform!(sdf -> (b = [1, 5],), copy(df)) == DataFrame(a=[1, 2], b=[1, 5], c=[5, 6]) @test transform!(sdf -> 1, copy(df)) == DataFrame(a=1:2, b=3:4, c=5:6, x1=1) @test transform!(sdf -> fill([1]), copy(df)) == DataFrame(a=1:2, b=3:4, c=5:6, x1=[[1], [1]]) @@ -1456,8 +1456,8 @@ end for ret in (DataFrame(), NamedTuple(), zeros(0, 0), DataFrame(t=1)[1, 1:0]) @test transform!(sdf -> ret, copy(df)) == df end - @test_throws ArgumentError transform!(sdf -> DataFrame(a=10), copy(df)) - @test_throws ArgumentError transform!(sdf -> zeros(1, 2), copy(df)) + @test_throws DimensionMismatch transform!(sdf -> DataFrame(a=10), copy(df)) + @test_throws DimensionMismatch transform!(sdf -> zeros(1, 2), copy(df)) @test transform!(sdf -> DataFrame(a=[10, 11]), copy(df)) == DataFrame(a=[10, 11], b=3:4, c=5:6) @test transform!(sdf -> [10 11; 12 13], copy(df)) == DataFrame(a=1:2, b=3:4, c=5:6, x1=[10, 12], x2=[11, 13]) @test transform!(sdf -> DataFrame(a=10)[1, :], copy(df)) == DataFrame(a=[10, 10], b=3:4, c=5:6) @@ -1489,7 +1489,7 @@ end @test combine(df, :a => (x -> res) => [:p, :q]) == DataFrame(p=1, q=2) @test_throws ArgumentError combine(df, :a => (x -> res) => [:p]) @test_throws ArgumentError select(df, :a => (x -> res) => AsTable) - @test_throws ArgumentError transform(df, :a => (x -> res) => AsTable) + @test_throws DimensionMismatch transform(df, :a => (x -> res) => AsTable) end @test combine(df, :a => ByRow(x -> [x, x+1]), :a => ByRow(x -> [x, x+1]) => AsTable, @@ -1625,8 +1625,8 @@ end DataFrame(a1=1, a2=2, a=1:2) @test select(df, :a => (x -> 1) => :a1, :a => (x -> 2) => :a2, [:a]) == DataFrame(a1=1, a2=2, a=1:2) - @test_throws ArgumentError combine(df, :a => (x -> 1) => :a1, :a => (x -> [2]) => :a2, [:a]) - @test_throws ArgumentError select(df, :a => (x -> 1) => :a1, :a => (x -> [2]) => :a2, [:a]) + @test_throws DimensionMismatch combine(df, :a => (x -> 1) => :a1, :a => (x -> [2]) => :a2, [:a]) + @test_throws DimensionMismatch select(df, :a => (x -> 1) => :a1, :a => (x -> [2]) => :a2, [:a]) end @testset "normalize_selection" begin @@ -2620,7 +2620,7 @@ end # multialias detection df = DataFrame(a=1:3) - df.b = df.a + @alias df.b = df.a df2 = select(df, [:a, :b] => ((x, y) -> x) => :c, :a, :b, copycols=false) @test df2.c === df.a === df.b @test df2.a == df2.b == df.a diff --git a/test/sort.jl b/test/sort.jl index 670249bcc6..c15e22e2fa 100644 --- a/test/sort.jl +++ b/test/sort.jl @@ -217,8 +217,8 @@ end # this works because 2:5 is immutable df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, 2:5) - df.x2 = view(x, 2:5) + @alias df.x1 = view(x, 2:5) + @alias df.x2 = view(x, 2:5) sort!(df) @test issorted(df) @test x == [10, 6, 7, 8, 9, 5, 4, 3, 2, 1] @@ -226,8 +226,8 @@ end df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, 2:5) - df.x2 = view(x, [2:5;]) + @alias df.x1 = view(x, 2:5) + @alias df.x2 = view(x, [2:5;]) sort!(df) @test_broken issorted(df) @test_broken x == [10, 6, 7, 8, 9, 5, 4, 3, 2, 1] @@ -235,8 +235,8 @@ end df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, 2:5) - df.x2 = view(x, 2:5) + @alias df.x1 = view(x, 2:5) + @alias df.x2 = view(x, 2:5) dfv = view(df, 2:4, :) sort!(dfv) @test issorted(dfv) @@ -245,8 +245,8 @@ end df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, [2:5;]) - df.x2 = view(x, 2:5) + @alias df.x1 = view(x, [2:5;]) + @alias df.x2 = view(x, 2:5) dfv = view(df, 2:4, :) sort!(dfv) @test_broken issorted(dfv) @@ -256,16 +256,16 @@ end # unsafe aliasing test df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, 2:5) - df.x2 = view(x, 3:6) + @alias df.x1 = view(x, 2:5) + @alias df.x2 = view(x, 3:6) sort!(df) @test_broken x == 10:-1:1 @test_broken df == DataFrame(x1=9:-1:6, x2=8:-1:5) df = DataFrame() x = [10:-1:1;] - df.x1 = view(x, 2:5) - df.x2 = view(x, 3:6) + @alias df.x1 = view(x, 2:5) + @alias df.x2 = view(x, 3:6) dfv = view(df, 2:4, :) sort!(dfv) @test_broken x == 10:-1:1 @@ -366,7 +366,7 @@ end sort!(sdf) @test df == dfc sort!(df, :id) - df.d = df.a + @alias df.d = df.a sort!(sdf) @test df[:, 1:4] == dfc end @@ -385,18 +385,18 @@ end @test df == DataFrame(a=[3, 2, 1, 4, 5], b=[4, 5, 6, 3, 2]) df = DataFrame(x1=rand(10)) - df.x2 = df.x1 - df.x3 = df.x1 - df.x4 = df.x1 - df.x5 = df.x1 + @alias df.x2 = df.x1 + @alias df.x3 = df.x1 + @alias df.x4 = df.x1 + @alias df.x5 = df.x1 sort!(df, :x4) @test issorted(df) df = DataFrame(x1=rand(10)) - df.x2 = df.x1 - df.x3 = df.x1 - df.x4 = df.x1 - df.x5 = df.x1 + @alias df.x2 = df.x1 + @alias df.x3 = df.x1 + @alias df.x4 = df.x1 + @alias df.x5 = df.x1 dfv = @view df[1:5, 1:4] sort!(dfv, :x4) @test issorted(dfv) diff --git a/test/subdataframe_mutation.jl b/test/subdataframe_mutation.jl index b4e7772774..4d754d627e 100644 --- a/test/subdataframe_mutation.jl +++ b/test/subdataframe_mutation.jl @@ -7,7 +7,7 @@ const ≅ = isequal @testset "mutating SubDataFrame with assignment to [!, col]" begin df = DataFrame() sdf = @view df[:, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(a=[]) @@ -20,7 +20,7 @@ const ≅ = isequal df = DataFrame() sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(a=[]) @@ -33,7 +33,7 @@ const ≅ = isequal df = DataFrame(x=Int[]) sdf = @view df[:, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(x=Int[], a=[]) @@ -52,7 +52,7 @@ const ≅ = isequal df = DataFrame(x=Int[]) sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(x=Int[], a=[]) @@ -71,7 +71,7 @@ const ≅ = isequal df = DataFrame(x=1:5) sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=missing) @@ -92,7 +92,7 @@ const ≅ = isequal df = DataFrame(x=1:5) sdf = @view df[:, :] - @test_throws ArgumentError sdf[!, :a] = [1] + @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :a] = 11:15 @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=11:15) @@ -158,9 +158,8 @@ const ≅ = isequal c=21:25, d=[missing, 102, 103, missing, missing], e=[missing, 1002, 1003, missing, missing]) - @test_throws ArgumentError sdf[!, :x] = 1 - @test_throws ArgumentError sdf[!, :x] = [1] - @test_throws ArgumentError sdf[!, :a] = 1 + @test_throws ArgumentError sdf[!, :x] .= 1 + @test_throws DimensionMismatch sdf[!, :x] = [1] @test_throws DimensionMismatch sdf[!, :a] = [1] sdf[!, :f] = categorical(["3", "2"]) @test df.f isa CategoricalArray @@ -210,7 +209,6 @@ const ≅ = isequal c=21:25) @test_throws ArgumentError sdf[!, :x] = 1 @test_throws ArgumentError sdf[!, :x] = [1] - @test_throws ArgumentError sdf[!, :a] = 1 @test_throws DimensionMismatch sdf[!, :a] = [1] @test_throws ArgumentError sdf[!, :f] = categorical(["3", "2"]) tmpc = df.c @@ -236,18 +234,25 @@ end @testset "mutating SubDataFrame with broadcasting assignment to [!, col]" begin df = DataFrame() sdf = @view df[:, :] - sdf[!, :a] .= [1] + sdf[!, :a] = 1 @test df.a isa Vector{Union{Missing, Int}} @test isempty(df.a) - sdf[!, :b] .= 1 + sdf[!, :b] = 1 @test df.b isa Vector{Union{Missing, Int}} @test isempty(df.b) - @test_throws DimensionMismatch sdf[!, :c] .= 1:2 + @test_throws DimensionMismatch sdf[!, :c] = 1:2 + @test_throws ArgumentError sdf[!, :c] .= 1:2 @test_throws DimensionMismatch sdf[!, :a] .= 1:2 sdf[!, :a] .= [1.0] - @test df.a isa Vector{Union{Missing, Float64}} + @test df.a isa Vector{Union{Missing, Int}} @test isempty(df.a) sdf[!, :b] .= 1.0 + @test df.b isa Vector{Union{Missing, Int}} + @test isempty(df.b) + sdf[!, :a] = 1.0 + @test df.a isa Vector{Union{Missing, Float64}} + @test isempty(df.a) + sdf[!, :b] = 1.0 @test df.b isa Vector{Union{Missing, Float64}} @test isempty(df.b) @@ -259,18 +264,27 @@ end df = DataFrame() sdf = @view df[1:0, :] - sdf[!, :a] .= [1] + sdf[!, :a] = 1 @test df.a isa Vector{Union{Missing, Int}} @test isempty(df.a) - sdf[!, :b] .= 1 + sdf[!, :b] = 1 @test df.b isa Vector{Union{Missing, Int}} @test isempty(df.b) - @test_throws DimensionMismatch sdf[!, :c] .= 1:2 + @test_throws DimensionMismatch sdf[!, :c] = 1:2 + @test_throws ArgumentError sdf[!, :c] .= 1:2 @test_throws DimensionMismatch sdf[!, :a] .= 1:2 sdf[!, :a] .= [1.0] - @test df.a isa Vector{Union{Missing, Float64}} + @test df.a isa Vector{Union{Missing, Int}} + @test isempty(df.a) + @test_throws DimensionMismatch sdf[!, :a] = [1.0] @test isempty(df.a) sdf[!, :b] .= 1.0 + @test df.b isa Vector{Union{Missing, Int}} + @test isempty(df.b) + sdf[!, :a] = 1.0 + @test df.a isa Vector{Union{Missing, Float64}} + @test isempty(df.a) + sdf[!, :b] = 1.0 @test df.b isa Vector{Union{Missing, Float64}} @test isempty(df.b) @@ -282,30 +296,30 @@ end df = DataFrame(x=Int[]) sdf = @view df[:, :] - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} df = DataFrame(x=Int[]) sdf = @view df[:, 1:end] - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} df = DataFrame(x=Int[]) sdf = @view df[1:0, :] - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} df = DataFrame(x=Int[]) sdf = @view df[1:0, 1:end] - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} df = DataFrame(x=1:5) sdf = @view df[1:0, :] - sdf[!, :a] .= [1] + sdf[!, :a] = 1 @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=missing) - sdf[!, :x] .= Nothing[] + sdf[!, :x] = Nothing[] @test df.x isa Vector{Union{Nothing, Int}} @test df ≅ DataFrame(x=1:5, a=missing) @@ -313,19 +327,19 @@ end sdf = @view df[1:0, 1:end] @test_throws ArgumentError sdf[!, :a] .= [1] @test df == DataFrame(x=1:5) - sdf[!, :x] .= Nothing[] + sdf[!, :x] = Nothing[] @test df.x isa Vector{Union{Nothing, Int}} @test df ≅ DataFrame(x=1:5) df = DataFrame(x=1:5) sdf = @view df[:, :] - sdf[!, :a] .= [1] + sdf[!, :a] = 1 @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=1) - sdf[!, :b] .= 2 + sdf[!, :b] = 2 @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=1, b=2) - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} @test df ≅ DataFrame(x=fill(nothing, 5), a=1, b=2) @@ -334,16 +348,16 @@ end @test_throws ArgumentError sdf[!, :a] .= [1] @test_throws ArgumentError sdf[!, :b] .= 2 @test df == DataFrame(x=1:5) - sdf[!, :x] .= nothing + sdf[!, :x] = nothing @test df.x isa Vector{Union{Nothing, Int}} @test df ≅ DataFrame(x=fill(nothing, 5)) df = DataFrame(a=1:5, b=11:15, c=21:25) sdf = @view df[[1, 3], :] - sdf[!, :d] .= 101 + sdf[!, :d] = 101 @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25, d=[101, missing, 101, missing, missing]) - sdf[!, :a] .= -1.0 + sdf[!, :a] = -1.0 @test eltype(df.a) === Float64 @test df ≅ DataFrame(a=[-1.0, 2, -1.0, 4, 5], b=11:15, c=21:25, @@ -353,7 +367,7 @@ end @test df ≅ DataFrame(a=[-1.0, 2, -2.0, 4, 5], b=11:15, c=21:25, d=[101, missing, 101, missing, missing]) - sdf[!, :e] .= 1:2 + sdf[!, :e] = 1:2 @test df ≅ DataFrame(a=[-1.0, 2, -2.0, 4, 5], b=11:15, c=21:25, d=[101, missing, 101, missing, missing], @@ -363,7 +377,7 @@ end sdf = @view df[[1, 3], 1:end] @test_throws ArgumentError sdf[!, :d] .= 101 @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25) - sdf[!, :a] .= -1.0 + sdf[!, :a] = -1.0 @test eltype(df.a) === Float64 @test df ≅ DataFrame(a=[-1.0, 2, -1.0, 4, 5], b=11:15, c=21:25) @@ -377,34 +391,37 @@ end df = DataFrame(a=1:5, b=11:15, c=21:25) sdf = @view df[[3, 2], :] - sdf[!, :d] .= 102 + sdf[!, :d] = 102 @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25, d=[missing, 102, 102, missing, missing]) - sdf[!, "e"] .= [1003, 1002] + sdf[!, "e"] = [1003, 1002] @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25, d=[missing, 102, 102, missing, missing], e=[missing, 1002, 1003, missing, missing]) - @test_throws ArgumentError sdf[!, 0] .= [10003, 10002] - @test_throws ArgumentError sdf[!, 6] .= 10002 + @test_throws BoundsError sdf[!, 0] .= [10003, 10002] + @test_throws ArgumentError sdf[!, 0] = [10003, 10002] + @test_throws BoundsError sdf[!, 6] .= 10002 + @test_throws ArgumentError sdf[!, 6] = 10002 @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25, d=[missing, 102, 102, missing, missing], e=[missing, 1002, 1003, missing, missing]) - sdf[!, 1] .= "10002" + sdf[!, 1] = "10002" @test eltype(df.a) === Any @test df ≅ DataFrame(a=[1, "10002", "10002", 4, 5], b=11:15, c=21:25, d=[missing, 102, 102, missing, missing], e=[missing, 1002, 1003, missing, missing]) - sdf[!, :b] .= [-13.0, -12.0] + sdf[!, :b] = [-13.0, -12.0] @test eltype(df.b) === Float64 @test df ≅ DataFrame(a=[1, "10002", "10002", 4, 5], b=[11, -12.0, -13.0, 14, 15], c=21:25, d=[missing, 102, 102, missing, missing], e=[missing, 1002, 1003, missing, missing]) - @test_throws DimensionMismatch sdf[!, :x] .= 1:3 + @test_throws ArgumentError sdf[!, :x] .= 1:3 + @test_throws DimensionMismatch sdf[!, :x] = 1:3 @test_throws DimensionMismatch sdf[!, :a] .= 1:3 - sdf[!, :f] .= categorical(["3", "2"]) + sdf[!, :f] = categorical(["3", "2"]) @test df.f isa CategoricalArray @test df ≅ DataFrame(a=[1, "10002", "10002", 4, 5], b=[11, -12.0, -13.0, 14, 15], @@ -413,7 +430,7 @@ end e=[missing, 1002, 1003, missing, missing], f=[missing, "2", "3", missing, missing]) tmpc = df.c - sdf[!, 3] .= [33, 22] + sdf[!, 3] = [33, 22] @test tmpc == 21:25 @test tmpc != df.c @test eltype(df.c) === Int @@ -423,7 +440,7 @@ end d=[missing, 102, 102, missing, missing], e=[missing, 1002, 1003, missing, missing], f=[missing, "2", "3", missing, missing]) - sdf[!, 3] .= categorical(["33", "22"])[2] + sdf[!, 3] = categorical(["33", "22"])[2] @test eltype(df.c) === Any @test df ≅ DataFrame(a=[1, "10002", "10002", 4, 5], b=[11, -12.0, -13.0, 14, 15], @@ -438,14 +455,18 @@ end sdf = @view df[[3, 2], 1:3] @test_throws ArgumentError sdf[!, :d] .= [103, 102] @test_throws ArgumentError sdf[!, "e"] .= [1003, 1002] - @test_throws ArgumentError sdf[!, 0] .= [10003, 10002] - @test_throws ArgumentError sdf[!, 6] .= [10003, 10002] + @test_throws BoundsError sdf[!, 0] .= [10003, 10002] + @test_throws BoundsError sdf[!, 6] .= [10003, 10002] + @test_throws ArgumentError sdf[!, 0] = [10003, 10002] + @test_throws ArgumentError sdf[!, 6] = [10003, 10002] @test df ≅ DataFrame(a=1:5, b=11:15, c=21:25) - sdf[!, 1] .= ["10003", "10002"] + sdf[!, 1] = ["10003", "10002"] @test eltype(df.a) === Any @test df ≅ DataFrame(a=[1, "10002", "10003", 4, 5], b=11:15, c=21:25) sdf[!, :b] .= -12.0 + @test eltype(df.b) === Int64 + sdf[!, :b] = -12.0 @test eltype(df.b) === Float64 @test df ≅ DataFrame(a=[1, "10002", "10003", 4, 5], b=[11, -12.0, -12.0, 14, 15], @@ -454,14 +475,14 @@ end @test_throws ArgumentError sdf[!, :x] .= [1] @test_throws ArgumentError sdf[!, :f] .= categorical(["3", "2"]) tmpc = df.c - sdf[!, 3] .= [33, 22] + sdf[!, 3] = [33, 22] @test tmpc == 21:25 @test tmpc != df.c @test eltype(df.c) === Int @test df ≅ DataFrame(a=[1, "10002", "10003", 4, 5], b=[11, -12.0, -12.0, 14, 15], c=[21, 22, 33, 24, 25]) - sdf[!, 3] .= categorical(["33", "22"])[2] + sdf[!, 3] = categorical(["33", "22"])[2] @test eltype(df.c) === Any @test df ≅ DataFrame(a=[1, "10002", "10003", 4, 5], b=[11, -12.0, -12.0, 14, 15], @@ -684,7 +705,7 @@ end @testset "mutating SubDataFrame with assignment to [:, col]" begin df = DataFrame() sdf = @view df[:, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(a=[]) @@ -697,7 +718,7 @@ end df = DataFrame() sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(a=[]) @@ -710,7 +731,7 @@ end df = DataFrame(x=Int[]) sdf = @view df[:, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(x=Int[], a=[]) @@ -729,7 +750,7 @@ end df = DataFrame(x=Int[]) sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df == DataFrame(x=Int[], a=[]) @@ -748,7 +769,7 @@ end df = DataFrame(x=1:5) sdf = @view df[1:0, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = Int[] @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=missing) @@ -769,7 +790,7 @@ end df = DataFrame(x=1:5) sdf = @view df[:, :] - @test_throws ArgumentError sdf[:, :a] = [1] + @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :a] = 11:15 @test df.a isa Vector{Union{Missing, Int}} @test df ≅ DataFrame(x=1:5, a=11:15) @@ -833,8 +854,8 @@ end c=21:25, d=[missing, 102, 103, missing, missing], e=[missing, 1002, 1003, missing, missing]) - @test_throws ArgumentError sdf[:, :x] = 1 - @test_throws ArgumentError sdf[:, :x] = [1] + @test_throws DimensionMismatch sdf[:, :x] = 1 + @test_throws DimensionMismatch sdf[:, :x] = [1] @test_throws MethodError sdf[:, :a] = 1 @test_throws DimensionMismatch sdf[:, :a] = [1] sdf[:, :f] = categorical(["3", "2"]) @@ -898,7 +919,7 @@ end c=[21, 22, 33, 24, 25]) sdf = @view df[[3, 2], 1:2] - @test_throws ArgumentError df[!, :c] = 1:2 + @test_throws DimensionMismatch df[!, :c] = 1:2 end @testset "mutating SubDataFrame with broadcasting assignment to [:, col]" begin @@ -1361,33 +1382,21 @@ end @test df ≅ DataFrame(a=[1.0, 12.0, 13.0]) end -@testset "mutating SubDataFrame with broadcasting assignment to sdf.col" begin +@testset "mutating SubDataFrame with assignment to sdf.col" begin df = DataFrame(a=1:3) sdf = @view df[[3, 2], :] - sdf.a .= 12.0 - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - @test eltype(sdf.a) === Float64 - else - @test eltype(sdf.a) === Int - end + sdf.a = 12.0 + @test eltype(sdf.a) === Float64 @test df ≅ DataFrame(a=[1, 12, 12]) - if VERSION >= v"1.7" - sdf.c .= 100 - @test df ≅ DataFrame(a=[1, 12, 12], c=[missing, 100, 100]) - else - @test_throws ArgumentError sdf.c .= 100 - end + sdf.c = 100 + @test df ≅ DataFrame(a=[1, 12, 12], c=[missing, 100, 100]) df = DataFrame(a=1:3) sdf = @view df[[3, 2], 1:1] @test_throws ArgumentError sdf.c = [5, 6] - sdf.a .= 12.0 - if isdefined(Base, :dotgetproperty) # Introduced in Julia 1.7 - @test eltype(sdf.a) === Float64 - else - @test eltype(sdf.a) === Int - end + sdf.a = 12.0 + @test eltype(sdf.a) === Float64 @test df ≅ DataFrame(a=[1, 12, 12]) end