Assignment 3: Sunrise, Sunset

Assigned Thursday, 8 February
Due Wednesday, 14 February, 11:59 p.m.
Sunset over the Pacific Ocean

Contents

Introduction

We love sunny days, and as winter begins to fade, the hours of daylight are beginning to grow. In this assignment, we’ll use our experience working with tables to process solar and lunar temporal information and to visualize how the hours of sunlight and moonlight vary over time.

Task: Since you’ll be working with tables, at the top of your file, add the line

include shared-gdrive("dcic-2021",
  "1wyQZj_L0qqV9Ekgr9au6RX2iqt2Ga8Ep")

And remember, for help with table functions, refer to this documentation, which goes with the dcic-2021 functions, rather than the official Pyret documentation.

Part 1: Data

Some kindly astronomers have shared a data set with us, which they’ve made available as a Google sheet.

3 points

Task: Copy the unique ID – the long string of letters, numbers, and underscores – in the spreadsheet URL and use it to load it into a Pyret table named celestial-data. Refer to the code for loading a table from a spreadsheet, seen in class and in the textbook.

To ensure the autograder works for your program, use the column names

year, month, day,
age-of-moon,
sun-rise, sun-culm, sun-set,
moon-rise, moon-culm, moon-set

We won’t use all of the columns in this spreadsheet. This is perfectly normal when working with real data sets!

1 point

Task: To test the functions we’ll write, we also need a small table of test data. Use the following table or make up your own examples:

test-data =
  table:
    year, month, day,
    sun-rise, sun-set,
    moon-rise, moon-set
    row: 2021, 9, 30, "06:30", "18:30", "00:00", "16:00"
    row: 2022, 3, 22, "07:00", "18:00", "23:00", "08:30"
  end

You should use your test-data table to write examples for each function in a where block. While the autograder won’t deduct points for missing tests, your instructor will! Doing so before you write the body of the function will ensure you understand how the function is supposed to work.

Part 2: Sunlight

To compute the hours of sunlight, we’ll need to know the elapsed time between sunrise and sunset. It’s complex to do this with times expressed as hours and minutes, e.g., "06:30", so we’ll first write a function to convert a time to minutes.

4 points

Task: Write a function

fun mins-since-midnight(time :: String) -> Number:
  ...
end

Given a time as a string (like those in the table), it should convert it to the number of minutes since midnight.

Hints:

  • For each function on the assignment, try writing examples in the where block before you write the body of the function. This ensures you understand how the function is supposed to work!
  • Since the times are strings, you may want to review the documentation listing the functions Pyret provides for working with strings. How could you find just the hours in the string? Just the minutes? Some of the string functions you’ll see in the documentation produce a list. Although you may have seen lists in class, you don’t need to use lists to solve this problem!
  • After you’ve identified the hours and minutes, you’ll need to use the function string-to-number. Despite its name, the output is not a number! It’s some number if the string can be converted and none if it can’t. For this assignment, the strings can always be converted to numbers, so you only need to worry about the some case. To get the number out, use .value:

    ››› string-to-number("06")
    some(6)
    ››› string-to-number("06").value
    6
    

Now that you can convert the sunrise and sunset times to minutes since midnight, it’s not too hard to compute how many minutes are in between.

4 points

Task: Write a function

fun elapsed-mins(start-time :: String, end-time :: String)
  -> Number:
  ...
end

It should compute the minutes the elapsed between the given start time and end time, provided as strings like those found in the table.

And now we’re ready to add a new column to the table containing the hours of sunlight.

4 points

Task: Make a table named celestial-data-sun that has a new column called "sunlight" that contains the number of hours of sunlight for each day.

Remember: To do this, you’ll need to define a function that you’ll pass as an input to build-column, and it will need to convert the elapsed minutes to hours.

Part 3: Moonlight

Now that we’ve computed the hours of sunlight, it seems like it should be simple to compute the hours of moonlight – just swap the column names "sun-rise" and "sun-set" for "moon-rise" and "moon-set".

This works for some of the rows in our table:

››› good-night = test-data.row-n(0)
››› elapsed-mins(good-night["moon-rise"], good-night["moon-set"])
960

But not for others:

››› bad-night = test-data.row-n(1)
››› elapsed-mins(bad-night["moon-rise"], bad-night["moon-set"])
-870

Why is this? The moon rose at 23:00 (i.e., 11 p.m.) on one day and set at 8:30 a.m. the next.

Let’s extend our code to handle this!

4 points

Task: Write a function

fun mins-before-midnight(time :: String) -> Number:
  ...
end

Don’t repeat the code from mins-since-midnight! Instead, think how you can use a call to that function in your definition. The body of this function only needs to be one line!

Now we can improve elapsed-mins. Let’s leave our original version (which works for sunlight) and define a new one:

4 points

Task: Write a function

fun elapsed-mins-improved(start-time :: String, end-time :: String)
  -> Number:
  ...
end

When the start time is before the end time, it should work just like elapsed-mins.

When the start time is after the end time (that is, it’s the next day), the function should instead take the minutes from the start time until midnight (which you just wrote a function to do) and add them to the minutes from midnight until the end time.

With elapsed-mins-improved, we’re ready to compute the hours of moonlight for each day in the table.

Task: Make a table named celestial-data-sun-moon that has a new column called "moonlight" that contains the number of hours of moonlight for each day.

Be sure you are starting with celestial-data-sun from Part 1! We want to end up with a single table that has columns for both hours of sunlight and hours of moonlight.

Part 4: Visualization (coach-free)

While we can look at the numbers we added to the table, it’s much nicer to get a graphic overview.

We’ll draw bar charts, but, unfortunately, none of the columns in the table give a good label for the bars. Since each bar is for one day, they should be labeled with the date.

3 points

Task: Make a new table, celestial-data-to-plot, that has a column "date" giving the abbreviated date for each row, e.g., "9/30" for September 30.

1 point

Task: Display bar charts for the hours of sunlight and moonlight. To do this, call

bar-chart(celestial-data-to-plot, "date", "sunlight")

and

bar-chart(celestial-data-to-plot, "date", "moonlight")

If everything’s worked right, you should see a very clear trend for the hours of sunlight and a very clear repeating pattern for hours of moonlight!

Challenge exercise (coach-free)

The spreadsheet we loaded the table from in Part 1 actually has two sheets. The one we used, celestial is a cleaned version of the original data, which is on the sheet named tricky.

In that original data, some of the times are missing. This is because it’s possible for moon rise, moon culm., or moon set to be on a different day (e.g., the moon rose the day before or set the following day). We removed these troublesome rows for you in celestial.

2 points

Task: Change your program to load its data from the tricky sheet. To make it still work, automatically remove the rows that are missing a time in any of the columns.

Check blocks (autograder compatibility)

Task: Copy the following check block into the bottom of your program and run it:

check "Tables exist and have the right names":
  celestial-data.row-n(0)
  test-data.row-n(0)
  celestial-data-sun.row-n(0)
  celestial-data-sun-moon.row-n(0)
  celestial-data-to-plot.row-n(0)
end

Task: Copy the following check block into the bottom of your program and run it:

check "Functions exist and have correct inputs":
  mins-since-midnight("00:00")
  elapsed-mins("00:00", "00:01")
  mins-before-midnight("00:00")
  elapsed-mins-improved("00:00", "00:01")
end

Assignment guidelines

6 points

As with Assignment 2, you are expected to follow good Pyret style, including writing docstrings and examples for each function. Review the Testing and Style Guidelines and ask questions if anything’s unclear!

Submitting the assignment

  1. Download your file (FileDownload) and ensure it’s named asmt03.arr.
  2. Upload your assignment on Gradescope.

Note: You can submit as many times as you want before the deadline. Only your latest submission will be graded.