Skip to content

Commit

Permalink
add cols kwarg to rename/rename! (#3380)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkamins authored Sep 25, 2023
1 parent 103fe0d commit 7aec87d
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 20 deletions.
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# DataFrames.jl v1.7.0 Release Notes

## New functionalities

* `rename` and `rename!` now allow to apply a function transforming
column names only to a subset of the columns specified by the `cols`
keyword argument
([#3380](https://github.com/JuliaData/DataFrames.jl/pull/3380))

# DataFrames.jl v1.6.1 Release Notes

## Bug fixes
Expand Down
42 changes: 29 additions & 13 deletions src/abstractdataframe/abstractdataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Compat.hasproperty(df::AbstractDataFrame, s::AbstractString) = haskey(index(df),
rename!(df::AbstractDataFrame, (from => to)::Pair...)
rename!(df::AbstractDataFrame, d::AbstractDict)
rename!(df::AbstractDataFrame, d::AbstractVector{<:Pair})
rename!(f::Function, df::AbstractDataFrame)
rename!(f::Function, df::AbstractDataFrame; cols=All())
Rename columns of `df` in-place.
Each name is changed at most once. Permutation of names is allowed.
Expand All @@ -132,8 +132,10 @@ Each name is changed at most once. Permutation of names is allowed.
- `df` : the `AbstractDataFrame`
- `d` : an `AbstractDict` or an `AbstractVector` of `Pair`s that maps
the original names or column numbers to new names
- `f` : a function which for each column takes the old name as a `String`
and returns the new name that gets converted to a `Symbol`
- `f` : a function which for each column selected by the `cols` keyword argument
takes the old name as a `String`
and returns the new name that gets converted to a `Symbol`; the `cols`
column selector can be any value accepted as column selector by the `names` function
- `vals` : new column names as a vector of `Symbol`s or `AbstractString`s
of the same length as the number of columns in `df`
- `makeunique` : if `false` (the default), an error will be raised
Expand Down Expand Up @@ -194,6 +196,14 @@ julia> rename!(uppercase, df)
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
julia> rename!(lowercase, df, cols=contains('A'))
1×3 DataFrame
Row │ a B a_1
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
```
"""
function rename!(df::AbstractDataFrame, vals::AbstractVector{Symbol};
Expand Down Expand Up @@ -252,12 +262,8 @@ end

rename!(df::AbstractDataFrame, args::Pair...) = rename!(df, collect(args))

function rename!(f::Function, df::AbstractDataFrame)
rename!(f, index(df))
# renaming columns of SubDataFrame has to clean non-note metadata in its parent
_drop_all_nonnote_metadata!(parent(df))
return df
end
rename!(f::Function, df::AbstractDataFrame; cols=All()) =
rename!(df, [n => Symbol(f(n)) for n in names(df, cols)])

"""
rename(df::AbstractDataFrame, vals::AbstractVector{Symbol};
Expand All @@ -267,7 +273,7 @@ end
rename(df::AbstractDataFrame, (from => to)::Pair...)
rename(df::AbstractDataFrame, d::AbstractDict)
rename(df::AbstractDataFrame, d::AbstractVector{<:Pair})
rename(f::Function, df::AbstractDataFrame)
rename(f::Function, df::AbstractDataFrame; cols=All())
Create a new data frame that is a copy of `df` with changed column names.
Each name is changed at most once. Permutation of names is allowed.
Expand All @@ -277,8 +283,10 @@ Each name is changed at most once. Permutation of names is allowed.
only allowed if it was created using `:` as a column selector.
- `d` : an `AbstractDict` or an `AbstractVector` of `Pair`s that maps
the original names or column numbers to new names
- `f` : a function which for each column takes the old name as a `String`
and returns the new name that gets converted to a `Symbol`
- `f` : a function which for each column selected by the `cols` keyword argument
takes the old name as a `String`
and returns the new name that gets converted to a `Symbol`; the `cols`
column selector can be any value accepted as column selector by the `names` function
- `vals` : new column names as a vector of `Symbol`s or `AbstractString`s
of the same length as the number of columns in `df`
- `makeunique` : if `false` (the default), an error will be raised
Expand Down Expand Up @@ -350,14 +358,22 @@ julia> rename(uppercase, df)
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
julia> rename(uppercase, df, cols=contains('x'))
1×3 DataFrame
Row │ i X y
│ Int64 Int64 Int64
─────┼─────────────────────
1 │ 1 2 3
```
"""
rename(df::AbstractDataFrame, vals::AbstractVector{Symbol};
makeunique::Bool=false) = rename!(copy(df), vals, makeunique=makeunique)
rename(df::AbstractDataFrame, vals::AbstractVector{<:AbstractString};
makeunique::Bool=false) = rename!(copy(df), vals, makeunique=makeunique)
rename(df::AbstractDataFrame, args...) = rename!(copy(df), args...)
rename(f::Function, df::AbstractDataFrame) = rename!(f, copy(df))
rename(f::Function, df::AbstractDataFrame; cols=All()) = rename!(f, copy(df); cols=cols)

"""
size(df::AbstractDataFrame[, dim])
Expand Down
2 changes: 0 additions & 2 deletions src/other/index.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ function rename!(x::Index, nms::AbstractVector{Pair{Symbol, Symbol}})
return x
end

rename!(f::Function, x::Index) = rename!(x, [(n=>Symbol(f(string(n)))) for n in x.names])

# we do not define keys on purpose;
# use names to get keys as strings with copying
# or _names to get keys as Symbols without copying
Expand Down
13 changes: 13 additions & 0 deletions test/dataframe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,19 @@ end
df = DataFrame(A=1)
asview && (df=view(df, :, :))
@test rename(x -> 1, df) == DataFrame(Symbol("1") => 1)

for cols in (:B, Not("A"), Cols(2), Char, contains('B'))
df = DataFrame(A=1:3, B='A':'C')
asview && (df = view(df, :, :))
@test names(rename(lowercase, df, cols=cols)) == ["A", "b"]
@test names(df) == ["A", "B"]
rename!(lowercase, df, cols=cols)
@test names(df) == ["A", "b"]
end
df = DataFrame(A=1:3, B='A':'C')
asview && (df = view(df, :, :))
@test names(rename(lowercase, df, cols=[:A, :B])) == ["a", "b"]
@test names(rename(lowercase, df, cols=Not(:))) == ["A", "B"]
end

sdf = view(DataFrame(ones(2, 3), :auto), 1:2, 1:3)
Expand Down
6 changes: 1 addition & 5 deletions test/index.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ using DataFrames: Index, SubIndex, fuzzymatch
@test_throws ArgumentError i[Not(:x)]
@test_throws ArgumentError i[Not("x")]
@test_throws BoundsError i[Not(1:3)]

@test i[Not([1, 1])] == [2]
@test i[Not([:A, :A])] == [2]
@test i[Not(["A", "A"])] == [2]
Expand Down Expand Up @@ -84,10 +84,6 @@ end
@test rename!(copy(i), [:a => :A]) == Index([:A, :b])
@test rename!(copy(i), [:a => :a]) == Index([:a, :b])
@test rename!(copy(i), [:a => :b, :b => :a]) == Index([:b, :a])
@test rename!(x -> Symbol(uppercase(string(x))), copy(i)) == Index([:A, :B])
@test rename!(x -> Symbol(lowercase(string(x))), copy(i)) == Index([:a, :b])
@test rename!(uppercase, copy(i)) == Index([:A, :B])
@test rename!(lowercase, copy(i)) == Index([:a, :b])

@test delete!(i, :a) == Index([:b])
push!(i, :C)
Expand Down

0 comments on commit 7aec87d

Please sign in to comment.