Create a dictionary using "if... else" in Julia?

Viewed 102

I'm working with a sample CSV file that lists nursing home residents' DOBs and DODs. I used those fields to calculate their age at death, and now I'm trying to create a dictionary that "bins" their age at death into groups. I'd like the bins to be 1-25, 26-50, 51-75, and 76-100.

Is there a concise way to make a Dict(subject_id, age, age_bin) using "if... else" syntax? For example: (John, 76, "76-100"), (Moira, 58, "51-75").

So far I have:

#import modules
using CSV
using DataFrames
using Dates

# Open, read, write desired files 
input_file = open("../data/FILE.csv", "r")
output_file = open("FILE_output.txt", "w")

# Use to later skip header line
file_flag = 0

for line in readlines(input_file)
    if file_flag==0
        global file_flag = 1
        continue
    end

    # Define what each field in FILE corresponds to
    line_array = split(line, ",")
    subject_id = line_array[2]
    gender = line_array[3]
    date_of_birth = line_array[4]
    date_of_death = line_array[5]

    # Get yyyy-mm-dd only (first ten characters) from fields 4 and 5:
    date_birth = date_of_birth[1:10]
    date_death = date_of_death[1:10]

    # Create DateFormat; use to calculate age
    date_format = DateFormat("y-m-d")
    age_days = Date(date_death, date_format) - Date(date_birth, date_format)
    age_years = round(Dates.value(age_days)/365.25, digits=0)

    # Use "if else" statement to determine values
    keys = age_years
    function values()
        if age_years <= 25
            return "0-25"
        elseif age_years <= 50
            return "26-50"
        elseif age_years <= 75
            return "51-75"
        else age_years < 100
            return "76-100"
        end
    end
    values()

    # Create desired dictionary
    age_death_dict = Dict(zip(keys, values()))

end 

Edit: or is there a better way to approach this using DataFrames?

1 Answers

To answer your question, " is there a concise way using if/else" -- probably not, given that you have 5 cases (age ranges) you have to account for. Suppose you have names and ages in two separate lists (which I assume you generate from your example code, although I can't see the input CSVs):

julia> name = ["John", "Mary", "Robert", "Cindy", "Beatrice"];
julia> ages = [24, 73, 75, 69, 90];
julia> function bin_age_ifelse(a)
           if a<1
               return "Invalid age"
           elseif 1<=a<=25
               return "1-25"
           elseif 25<a<=50
               return "26-50"
           elseif 50<a<=75
               return "51-75"
           else
               return "76-100"
           end
       end
bin_age_ifelse (generic function with 1 method)


julia> binned_ifelse = Dict([n=>[a, bin_age_ifelse(a)] for (n,a) in zip(name, ages)])
Dict{String, Vector{Any}} with 5 entries:
  "John"     => [24, "1-25"]
  "Mary"     => [73, "51-75"]
  "Beatrice" => [90, "76-100"]
  "Robert"   => [75, "51-75"]
  "Cindy"    => [69, "51-75"]

Here's an option for the binning function to avoid if/else syntax, although there are probably yet more elegant ways to do it:

julia> function bin_age(a)
           bins = [1:25, 26:50, 51:75, 76:100]
           for b in bins
               if a in b
                   return "$(b[1])-$(b[end])"
               end
           end
        end
bin_age (generic function with 1 method)

julia> bin_age(84)
"76-100"

I've taken some liberties with the format of the answer, using the name as the key, since your original question describes a dict format that doesn't really make sense in Julia. If you'd like to have the keys be the age ranges, you could construct the dictionary above and then invert it as described here (with some modification since the values above have two entries).

If you don't care about name, age, or age range being a key, then I would suggest using DataFrames.jl:

julia> using DataFrames
  
julia> d = DataFrame(name=name, age=ages, age_range=[bin_age(a) for a in ages])
5×3 DataFrame
 Row │ name      age    age_range 
     │ String    Int64  String    
─────┼────────────────────────────
   1 │ John         24  1-25
   2 │ Mary         73  51-75
   3 │ Robert       75  51-75
   4 │ Cindy        69  51-75
   5 │ Beatrice     90  76-100
Related