diff --git a/NEWS.md b/NEWS.md index d2b2211504..ead8651853 100644 --- a/NEWS.md +++ b/NEWS.md @@ -35,7 +35,9 @@ ([#2308](https://github.com/JuliaData/DataFrames.jl/pull/2308)) * allow column renaming in joins ([#2313](https://github.com/JuliaData/DataFrames.jl/pull/2313) - * add `rownumber` to `DataFrameRow` ([#2356](https://github.com/JuliaData/DataFrames.jl/pull/2356)) +* add `rownumber` to `DataFrameRow` ([#2356](https://github.com/JuliaData/DataFrames.jl/pull/2356)) +* allow passing column name to specify the position where a new columns should be + inserted in `insertcols!` ([#2365](https://github.com/JuliaData/DataFrames.jl/pull/2365)) ## Deprecated diff --git a/src/dataframe/dataframe.jl b/src/dataframe/dataframe.jl index 6bd1b59343..6a0f49266d 100644 --- a/src/dataframe/dataframe.jl +++ b/src/dataframe/dataframe.jl @@ -603,18 +603,20 @@ end ############################################################################## """ - insertcols!(df::DataFrame, [ind::Int], (name=>col)::Pair...; + insertcols!(df::DataFrame, [col], (name=>val)::Pair...; makeunique::Bool=false, copycols::Bool=true) Insert a column into a data frame in place. Return the updated `DataFrame`. -If `ind` is omitted it is set to `ncol(df)+1` +If `col` is omitted it is set to `ncol(df)+1` (the column is inserted as the last column). # Arguments - `df` : the DataFrame to which we want to add columns -- `ind` : a position at which we want to insert a column +- `col` : a position at which we want to insert a column, passed as an integer + or a column name (a string or a `Symbol`); the column selected with `col` + and columns following it are shifted to the right in `df` after the operation - `name` : the name of the new column -- `col` : an `AbstractVector` giving the contents of the new column or a value of any +- `val` : an `AbstractVector` giving the contents of the new column or a value of any type other than `AbstractArray` which will be repeated to fill a new vector; As a particular rule a values stored in a `Ref` or a `0`-dimensional `AbstractArray` are unwrapped and treated in the same way. @@ -623,7 +625,7 @@ If `ind` is omitted it is set to `ncol(df)+1` be generated by adding a suffix - `copycols` : whether vectors passed as columns should be copied -If `col` is an `AbstractRange` then the result of `collect(col)` is inserted. +If `val` is an `AbstractRange` then the result of `collect(val)` is inserted. # Examples ```jldoctest @@ -655,8 +657,9 @@ julia> insertcols!(d, 2, :c => 2:4, :c => 3:5, makeunique=true) │ 3 │ 'c' │ 4 │ 5 │ 3 │ ``` """ -function insertcols!(df::DataFrame, col_ind::Int, name_cols::Pair{Symbol,<:Any}...; +function insertcols!(df::DataFrame, col::ColumnIndex, name_cols::Pair{Symbol,<:Any}...; makeunique::Bool=false, copycols::Bool=true) + col_ind = Int(col isa SymbolOrString ? columnindex(df, col) : col) if !(0 < col_ind <= ncol(df) + 1) throw(ArgumentError("attempt to insert a column to a data frame with " * "$(ncol(df)) columns at index $col_ind")) @@ -744,9 +747,9 @@ function insertcols!(df::DataFrame, col_ind::Int, name_cols::Pair{Symbol,<:Any}. return df end -insertcols!(df::DataFrame, col_ind::Int, name_cols::Pair{<:AbstractString,<:Any}...; +insertcols!(df::DataFrame, col::ColumnIndex, name_cols::Pair{<:AbstractString,<:Any}...; makeunique::Bool=false, copycols::Bool=true) = - insertcols!(df, col_ind, (Symbol(n) => v for (n,v) in name_cols)..., + insertcols!(df, col, (Symbol(n) => v for (n,v) in name_cols)..., makeunique=makeunique, copycols=copycols) insertcols!(df::DataFrame, name_cols::Pair{Symbol,<:Any}...; @@ -758,10 +761,10 @@ insertcols!(df::DataFrame, name_cols::Pair{<:AbstractString,<:Any}...; insertcols!(df, (Symbol(n) => v for (n,v) in name_cols)..., makeunique=makeunique, copycols=copycols) -function insertcols!(df::DataFrame, col_ind::Int=ncol(df)+1; makeunique::Bool=false, name_cols...) - if !(0 < col_ind <= ncol(df) + 1) +function insertcols!(df::DataFrame, col::Int=ncol(df)+1; makeunique::Bool=false, name_cols...) + if !(0 < col <= ncol(df) + 1) throw(ArgumentError("attempt to insert a column to a data frame with " * - "$(ncol(df)) columns at index $col_ind")) + "$(ncol(df)) columns at index $col")) end if !isempty(name_cols) # an explicit error is thrown as keyword argument was supported in the past diff --git a/test/dataframe.jl b/test/dataframe.jl index 2346ff8e23..991ec412da 100644 --- a/test/dataframe.jl +++ b/test/dataframe.jl @@ -187,8 +187,16 @@ end df = DataFrame(a=Union{Int, Missing}[1, 2], b=Union{Float64, Missing}[3.0, 4.0]) @test_throws ArgumentError insertcols!(df, 5, :newcol => ["a", "b"]) @test_throws ArgumentError insertcols!(df, 0, :newcol => ["a", "b"]) + @test_throws ArgumentError insertcols!(df, :z, :newcol => ["a", "b"]) + @test_throws ArgumentError insertcols!(df, "z", :newcol => ["a", "b"]) + @test_throws MethodError insertcols!(df, true, :newcol => ["a", "b"]) @test_throws DimensionMismatch insertcols!(df, 1, :newcol => ["a"]) + @test_throws DimensionMismatch insertcols!(df, :a, :newcol => ["a"]) + @test_throws DimensionMismatch insertcols!(df, "a", :newcol => ["a"]) + ref1 = insertcols!(copy(df), :a, :newcol => ["a", "b"]) + ref2 = insertcols!(copy(df), "a", :newcol => ["a", "b"]) @test insertcols!(df, 1, :newcol => ["a", "b"]) == df + @test ref1 == ref2 == df @test names(df) == ["newcol", "a", "b"] @test df.a == [1, 2] @test df.b == [3.0, 4.0]