What is the right way to transform multiple columns of a DataFrame in place in Julia?

Viewed 582

How should I apply a function to every element of some columns in place?

julia> using DataFrames

julia> df = DataFrame(Time = [3, 4, 5], TopTemp = [70, 73, 100], BottomTemp = [50, 55, 80])
3×3 DataFrame
 Row │ Time   TopTemp  BottomTemp 
     │ Int64  Int64    Int64      
─────┼────────────────────────────
   1 │     3       70          50
   2 │     4       73          55
   3 │     5      100          80

julia> fahrenheit_to_celsius(x) = Int(round((x - 32) * 5 / 9))
fahrenheit_to_celsius (generic function with 1 method)

This works for one column, but I'm not sure it is the best method.

julia> transform!(df, "TopTemp" => ByRow(fahrenheit_to_celsius), renamecols = false)
3×3 DataFrame
 Row │ Time   TopTemp  BottomTemp 
     │ Int64  Int64    Int64      
─────┼────────────────────────────
   1 │     3       21          50
   2 │     4       23          55
   3 │     5       38          80

The same method does not work to convert both columns using a regular expression with broadcasting.

julia> transform!(df, r"Temp" .=> ByRow.(fahrenheit_to_celsius), renamecols = false)
ERROR: LoadError: MethodError: no method matching fahrenheit_to_celsius(::Int64, ::Int64)
1 Answers

Use:

transform!(df, names(df, r"Temp") .=> ByRow(fahrenheit_to_celsius), renamecols = false)

or

df[!, r"Temp"] .= fahrenheit_to_celsius.(df[!, r"Temp"])

also note that it is not in-place, but the columns are replaced in both cases above but this is probably what you want in general.

An in-place operation would be:

df[:, r"Temp"] .= fahrenheit_to_celsius.(df[!, r"Temp"])

but it would fail if fahrenheit_to_celsius would change eltype of columns.

The fastest should be:

foreach(col -> col .= fahrenheit_to_celsius.(col), eachcol(df[!, r"Temp"])))
Related