# DataFrames: convert column data type

**URL:** <https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522>\
**Category:** Data\
**Tags:** type, dataframes, convert\
**Created:** [March 4, 2020, 3:03pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522 "2020-03-04T15:03:29Z")\
**Posts on this page:** 20\
**Page:** 3

<div class="post-metadata">

**Author:** ![FPGro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fpgro/32/20822_2.png) [@FPGro](https://discourse.julialang.org/u/FPGro)\
**Post date:** [March 7, 2021, 2:49am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/41 "2021-03-07T02:49:00Z")

</div>

I can absolutely see how this is confusing, just to explain where that comes from:

- `Int` is a type, it’s an alias to `Int64` or `Int32` as appropriate, `Int(...)` is therefore a constructor
- `float` on the other hand is an ordinary function and there is no type or alias `Float`, so that can’t work. `float(...)` tries to infer an appropriate floating point type to represent the input
- String is a peculiar case. `String` is a type obviously, and `String(...)` does work but it’s doing something else than you may expect. The constructor takes only things that can directly be converted to the String type and gives you a `String` object. So this works for codepoints and other String-like objects. `string(...)` on the other hand will turn anything into a string, and is therefore different from `String(...)`, similar how you get an `Int` from a `String` via `parse(somestring)`, not `Int(somestring)`

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 7, 2021, 12:20pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/42 "2021-03-07T12:20:03Z")

</div>

I appreciate your empathy and your explanation @FPGro 🙂 I am now reading up on constructors. It would still be nice to know:

1. why is there no constructor/alias to `Float64` or `Float32` called `Float`?

2. why is there no generic function with n methods called `int`?

---

<div class="post-metadata">

**Author:** ![FPGro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fpgro/32/20822_2.png) [@FPGro](https://discourse.julialang.org/u/FPGro)\
**Post date:** [March 7, 2021, 1:46pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/43 "2021-03-07T13:46:38Z")

</div>

Good questions! I’m not “old” enough with julia to answer that in full, but there used to be `Float` and `int` which were dropped at some point, so there’s probably a good reason for both 😃

[https://github.com/JuliaLang/julia/issues/1231](https://github.com/JuliaLang/julia/issues/1231)

[https://github.com/JuliaLang/julia/issues/1470](https://github.com/JuliaLang/julia/issues/1470)

Those may be good starts if you want to dig deeper

---

<div class="post-metadata">

**Author:** ![nalimilan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nalimilan/32/147_2.png) [@nalimilan](https://discourse.julialang.org/u/nalimilan)\
**Post date:** [March 7, 2021, 2:33pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/44 "2021-03-07T14:33:59Z")

</div>

> [@path-doc](#):
>
> 1. why is there no constructor/alias to `Float64` or `Float32` called `Float` ?

Even 32-bit CPUs use `Float64` as the default floating point type, so there’s no need for a `Float` alias like for `Int`.

> [@path-doc](#):
>
> - why is there no generic function with n methods called `int` ?

What would be the point of adding it? We already have `Int`. `string` and `float` are necessary evils, not something that we want to replicate for all types if we can avoid them.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [March 7, 2021, 3:33pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/45 "2021-03-07T15:33:55Z")

</div>

> [@path-doc](#):
>
> This is false, actually `Int.(df[!,:B])` will convert a String array into an Int64 array.

> [@path-doc](#):
>
> In contrast, `int(x)` does not work whereas `Int(x)` does.

I don’t understand these comments: `Int("23")` does not work. And the following also doesn’t work:

```julia
using DataFrames

df = DataFrame(B=["23"])
Int.(df[!,:B])

```

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 8, 2021, 12:14am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/46 "2021-03-08T00:14:50Z")

</div>

> [@nalimilan](#):
>
> What would be the point of adding it? We already have `Int` . `string` and `float` are necessary evils, not something that we want to replicate for all types if we can avoid them.

Consistency really, that’s all. From the perspective of a user that is in the middle of a project, with no time to study constructors, Python’s user-friendly `df[['B']].astype('T')` is ugly, but effective.

PS. As we can see from the discussion around the functioning of `Int` with @sijo there is also a form of “history dependence” that is unexpected. A “Markov” approach (vis a vis the state of the data) to the behaviour of these functions/constructors would also help with consistency. I accept that Python’s function may also be subject to this criticism (I haven’t tested it).

PPS. For comparison, I have just tested Python. Whether I run @sijo’s experiment or my own, `df3[['B']].astype('int')`  
`df3[['B']].astype('int64')`  
`df3[['B']].astype('float')`  
`df3[['B']].astype('str')`  
all work as expected. I think this kind of robustness is essential.

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 8, 2021, 12:17am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/47 "2021-03-08T00:17:14Z")

</div>

What can I say, except that it works for me, and that it fits the bill for what constructors are supposed to do.

Correction: I think I know why it works. In my experiment, I started with an array of integers and then converted it to an array of strings. Then I convert it back to integers. (May sound like something that one would never do in the field, but I needed it last year in a project on R. It is one of the reasons I am looking into Julia.

In your experiment, you start with strings, so I guess that “Julia” is trying to force you into using `parse`.

---

<div class="post-metadata">

**Author:** ![sijo](https://avatars.discourse-cdn.com/v4/letter/s/da6949/32.png) [@sijo](https://discourse.julialang.org/u/sijo)\
**Post date:** [March 8, 2021, 7:53am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/48 "2021-03-08T07:53:53Z")

</div>

Probably you called `Int` on values that were already numbers (not strings). Prove me wrong 🙂

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 8, 2021, 10:25am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/49 "2021-03-08T10:25:53Z")

</div>

DIY:

```julia
df = DataFrame(B = [23])
string.(df[!,:B])
Int.(df[!,:B])

```

QED 🙂

---

<div class="post-metadata">

**Author:** ![Skoffer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/skoffer/32/378_2.png) [@Skoffer](https://discourse.julialang.org/u/Skoffer)\
**Post date:** [March 8, 2021, 10:37am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/50 "2021-03-08T10:37:43Z")

</div>

Your second line do not change dataframe.

```julia
julia> df = DataFrame(B = [23])
1×1 DataFrame
 Row │ B
     │ Int64
─────┼───────
   1 │ 23

julia> string.(df[!,:B])
1-element Vector{String}:
 "23"

julia> df[!, :B]
1-element Vector{Int64}:
 23

```

If you want to make a change, you should reassign column

```julia
julia> df[!, :B] = string.(df[!,:B])
1-element Vector{String}:
 "23"

julia> df[!, :B]
1-element Vector{String}:
 "23"

```

Of course, `Int.` is not working anymore

```julia
julia> Int.(df[!,:B])
ERROR: MethodError: no method matching Int64(::String)

```

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 8, 2021, 11:32am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/51 "2021-03-08T11:32:58Z")

</div>

Thanks @Skoffer and @sijo and apologies to all for the confusion. I stand corrected. `Int` only works on numbers (not strings).

To round up,

- only `parse(T,x)` will convert strings to numbers (and parse only accepts strings).

- The constructor `String(x)` will only work on strings.

- The constructor `Int(x)` and `Float64(x)` etc only work on numbers.

- `convert(T,x)` is a conservative alternative to constructors

- `string(x)` works on anything

- All my comments on “history dependence” are nonsense. The “state” of the data is “Markovian”, just as it should be.

Final whinge: I think a lot of this would be much clearer if the domain and range of each function were clearly specified somewhere. All this `T(x)` in the manual would be much improved by writing T : X \rightarrow Y.

Thanks all. I guess you can’t make an omelette without cracking a few eggs. 🙂

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [March 8, 2021, 2:49pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/52 "2021-03-08T14:49:09Z")

</div>

I also don’t really feel the difference between constructors (`Int(x)`, `Float64(x)`, `String(x)`) and the `convert(T, x)` function: in practice both convert from one type to another when these types represent “the same” kind of thing (e.g. number to number).

The distinction between constructors and functions like `string(x)`, `parse(T, x)` is pretty clear, however: `string` and `parse` translate between conceptually different kinds of things, e.g. strings and numbers. There is no uniquely natural way to do this conversion in general: for example, what base should the string \<-\> number conversion use?

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 9, 2021, 7:19am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/53 "2021-03-09T07:19:13Z")

</div>

There is something about being able to call `convert` implicitly in the manual. (I have not been able to find a definition or example of “implicit call” however.)

The following link (thanks to @FPGro for posting it) has a pretty good discussion about the process that went into `convert` and constructors  
[https://github.com/JuliaLang/julia/issues/1470](https://github.com/JuliaLang/julia/issues/1470)

That discussion also covers consistency concerns and many of the points made here. Including a relevant comment by Stefan Karpinski on concern for newcomers 🙂 I guess he lost that battle.

---

<div class="post-metadata">

**Author:** ![FPGro](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fpgro/32/20822_2.png) [@FPGro](https://discourse.julialang.org/u/FPGro)\
**Post date:** [March 9, 2021, 10:19am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/54 "2021-03-09T10:19:29Z")

</div>

I think the implicit calling just refers to these cases:

[https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?](https://docs.julialang.org/en/v1/manual/conversion-and-promotion/#When-is-convert-called?)

So when you do:

```julia
julia> struct IntInDisguise
         the::Int
       end

julia> IntInDisguise(1)
IntInDisguise(1)

julia> IntInDisguise(1.)
IntInDisguise(1)

julia> IntInDisguise("1")
ERROR: MethodError: Cannot `convert` an object of type String to an object of type Int64
Closest candidates are:
  convert(::Type{T}, ::T) where T<:Number at number.jl:6
  convert(::Type{T}, ::Number) where T<:Number at number.jl:7
  convert(::Type{T}, ::Base.TwicePrecision) where T<:Number at twiceprecision.jl:250
  ...
Stacktrace:
 [1] IntInDisguise(the::String)
   @ Main ./REPL[1]:2
 [2] top-level scope
   @ REPL[4]:1

```

The constructor does call convert although `convert` never directly appears in the constructor. There’s really nothing more to it, you can extend these as normal: (don’t do that in real code)

```julia
julia> Base.convert(::Type{Int}, s::String) = parse(Int,s)

julia> IntInDisguise("1")
IntInDisguise(1)

```

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 9, 2021, 12:30pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/55 "2021-03-09T12:30:38Z")

</div>

very nice

---

<div class="post-metadata">

**Author:** ![path-doc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/path-doc/32/19183_2.png) [@path-doc](https://discourse.julialang.org/u/path-doc)\
**Post date:** [March 9, 2021, 1:33pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/56 "2021-03-09T13:33:19Z")

</div>

The answer to the following question is also very good.  
[https://stackoverflow.com/questions/12036037/explicit-call-to-a-constructor](https://stackoverflow.com/questions/12036037/explicit-call-to-a-constructor)  
From the comments in the Julia manual (the page you cite) `convert` can also be implicitly called, so it seems natural to conclude that `T(x)` are explicit, one-argument constructors.

---

<div class="post-metadata">

**Author:** ![jakewilliami](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakewilliami/32/18945_2.png) [@jakewilliami](https://discourse.julialang.org/u/jakewilliami)\
**Post date:** [August 15, 2021, 12:02am UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/57 "2021-08-15T00:02:36Z")

</div>

Without actually changing the data in the column (only its union type), I have written this little function to do that:

```julia
function add_type!(df::DataFrame, colname::Symbol, appendtypes::Type...)
    df[!, colname] =
        Vector{Union{appendtypes..., Base.uniontypes(eltype(df[!, colname]))...}}(df[!, colname])
    return df
end

```

This way, you can add types as a Union type to the column of interest, which will allow you to later add values of that type to the column without getting errors.

---

<div class="post-metadata">

**Author:** ![klwlevy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klwlevy/32/25272_2.png) [@klwlevy](https://discourse.julialang.org/u/klwlevy)\
**Post date:** [February 11, 2022, 5:33pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/58 "2022-02-11T17:33:38Z")

</div>

Thanks pdeffebach, that is very useful information. (`passmissing`)  
Is there even a way that works when there are 100% missings?

```julia
using DataFrames, Dates
#Vector of string dates
df_str_date = DataFrame(date_ = ["2022-02-10", "2022-02-11"])
#Convert date_ var from type String to type Date
df_str_date.date_ = parse.(Date, df_str_date[:,:date_]) # Type of date_ now Date, great

#Vector of string dates, including missing values
df_str_date2 = DataFrame(date_ = ["2022-02-10", Missing()])
#Convert date_ var from type String to type Date
df_str_date2.date_ = passmissing(parse).(Date, df_str_date2.date_) # Type of date_ now Union{Missing, Date}. Missings.passmissing does the trick, 

#Vector of "string" dates, 100% missing values
df_str_date3 = DataFrame(date_ = [Missing(), Missing()])
df_str_date3.date_ = passmissing(parse).(Date, df_str_date3.date_) #Type of date_ still Missing, not Union{Missing, Date} as I had hoped

```

---

<div class="post-metadata">

**Author:** ![pdeffebach](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pdeffebach/32/10320_2.png) [@pdeffebach](https://discourse.julialang.org/u/pdeffebach)\
**Post date:** [February 11, 2022, 6:08pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/59 "2022-02-11T18:08:20Z")

</div>

Best I can think of is

```julia
julia> t = [missing, missing]
2-element Vector{Missing}:
 missing
 missing

julia> Union{Date, Missing}[passmissing(parse)(ti) for ti in t]
2-element Vector{Union{Missing, Date}}:
 missing
 missing

```

---

<div class="post-metadata">

**Author:** ![nilshg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nilshg/32/2283_2.png) [@nilshg](https://discourse.julialang.org/u/nilshg)\
**Post date:** [February 11, 2022, 9:16pm UTC](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522/60 "2022-02-11T21:16:43Z")

</div>

Although I guess at that point you could just write

```julia
julia> missings(Date, 2)
2-element Vector{Union{Missing, Date}}:
 missing
 missing

```

given that you have to manually specify things here anyway (there’s no way to tell you want a `Date` column when everything is missing other than explicitly saying you do)

EDIT ah sorry I guess what you proposed could be a function that always produces a Date column, like this:

```julia
julia> always_parse_Date(x) = identity.(Union{Missing, Date}[passmissing(parse)(Date, xᵢ) for xᵢ ∈ x])
always_parse_Date (generic function with 1 method)

julia> always_parse_Date(["2020-1-1", "2020-1-2"])
2-element Vector{Date}:
 2020-01-01
 2020-01-02

julia> always_parse_Date(["2020-1-1", missing])
2-element Vector{Union{Missing, Date}}:
 2020-01-01
 missing

julia> always_parse_Date([missing, missing])
2-element Vector{Union{Missing, Date}}:
 missing
 missing

```

[Previous page](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522.md?page=2)

[Next page](https://discourse.julialang.org/t/dataframes-convert-column-data-type/35522.md?page=4)
