I am learning Ocaml and as part of an exercise, I have to create a function that returns the following day of a couple (day, month, year).
I did the function, but the software that evaluates my code tells me I'm using too much if ... then ... else ...
I haven't found a solution for several hours. Can someone help me ?
let leap = fun x -> x mod 4 = 0 && x mod 100 <> 0 || x mod 400 = 0
let nbDay(month,year) = match monthwith
| 1 | 3 | 5 | 7 | 8 | 10 | 12 -> 31
| 4 | 6 | 9 | 11 -> 30
| 2 -> if(leap(year)) then 29 else 28
| _ -> 0
let valid (day, month, year) = day <= nbDay(month,year)
&& day>= 1
&& month<=12
&& month>= 1
let tomorrow (day, month, year) = if(valid (day, month, year))
then if(day= nbDay(month,year))
then
if(month= 12)
then
let day= 1 in
let month= 1 in
let year= year+ 1 in
(day, month, year)
else
let day= 1 in
let month= month+ 1 in
(day, month, year)
else
let day= day+ 1 in
(day, month, year)
else failwith "invalid date!"
You shall use match more, for example, instead of writing
if month = 12 then xxx ...
you can write
match month with
| 12 -> xxx
...
When your condition is more complex, you can use when, e.g., instead of writing
| 2 -> if is_leap year then x else y
you can write
| 2 when is_leap year -> x
| 2 -> y
Related
| month | year | amount|
|-------|--------|-------|
| 1 | 2010 | 26 |
| 1 | 2010 | 26 |
| 2 | 2010 | 30 |
| 3 | 2010 | 35 |
| 3 | 2010 | 35 |
I need to figure out how to make another variable, that takes the prior month amount _n-1 and _n and divide it by 2, kind of like a moving average. The problem is that I need to do it by month and year, since there are multiples of the same month and year. There are other variables as well that are irrelevant, but that is why I can't just delete duplicates.
For example, for observation 5, I would need it to be (35+30+26) / 3
Your prescription and your example don't match at all. Your example is a mean of 3 monthly means, this month and the two previous. Your prescription is a month and the month previous.
Here is some technique that focuses on two possible meanings of your prescription.
* Example generated by -dataex-. For more info, type help dataex
clear
input byte month int year byte amount
1 2010 26
1 2010 26
2 2010 30
3 2010 35
3 2010 35
end
gen mdate = ym(year, month)
format mdate %tm
foreach w in total mean count {
egen `w' = `w'(amount), by(mdate)
}
gen wanted1 = (mean + mean[_n-1]) / 2 if mdate == mdate[_n-1] + 1
bysort mdate (wanted1) : replace wanted1 = wanted1[_n-1] if missing(wanted1)
gen wanted2 = (total + total[_n-1]) / (count + count[_n-1]) if mdate == mdate[_n-1] + 1
bysort mdate (wanted2) : replace wanted2 = wanted2[_n-1] if missing(wanted2)
list, sepby(mdate)
+----------------------------------------------------------------------------+
| month year amount mdate total mean count wanted1 wanted2 |
|----------------------------------------------------------------------------|
1. | 1 2010 26 2010m1 52 26 2 . . |
2. | 1 2010 26 2010m1 52 26 2 . . |
|----------------------------------------------------------------------------|
3. | 2 2010 30 2010m2 30 30 1 28 27.33333 |
|----------------------------------------------------------------------------|
4. | 3 2010 35 2010m3 70 35 2 32.5 33.33333 |
5. | 3 2010 35 2010m3 70 35 2 32.5 33.33333 |
+----------------------------------------------------------------------------+
I've 2 data sets, one which represts a list of all of the customers and other with their order dates
The order date are in a yyyyweek_number format, so for instance as today (2020-09-29) is week 40, the order date would be represented as 202040
I want to get a list of dealers who haven't placed orders in 4 day ranges viz. 30 days or less
60 days or less
90 days or less and
90+ days
To illustrate lets say the customer dataset is as under:
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| 13 |
| 14 |
| 15 |
+----+
and the Order table is as under:
+----+-----------------+
| ID | Order_YYYY_WEEK |
+----+-----------------+
| 1 | 202001 |
| 2 | 202003 |
| 3 | 202004 |
| 5 | 202006 |
| 2 | 202008 |
| 3 | 202010 |
| 6 | 202012 |
| 8 | 202009 |
| 1 | 202005 |
| 10 | 202015 |
| 11 | 202018 |
| 13 | 202038 |
| 15 | 202039 |
| 12 | 202040 |
+----+-----------------+
The slicer format that I've looks like this
Now say for instance the 30 days or less button is selected,
the resulting table should represent a table as under, with all the ID's from the Customer table that aren't present in the ORDER table where ORDER_YYYY_WEEK is 30 days from todays week
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
| 14 |
+----+
Steps:
Create relationship between Customer id's in Customer table and Order table (if not already there)
Create a Date table
Convert Weeks to dates in a new calculated column in the Order table
Create relationship between Customer id's in Customer table and Order table
Create relationship between Dates in Date table and Order table
Create calculated column in Date Table with Day ranges ("30 days or less" etc)
Create measure to identify if an order was placed
Add slicer with date range from Date table and table visual with Customer id.
Add measure to table visual on filter pane and set to "No"
Some of these steps have additional detail below.
2. Create a Date table
We can do this is PowerQuery or in DAX. Here's the DAX version:
Calendar =
VAR
Days = CALENDAR ( DATE ( 2020, 1, 1 ), DATE ( 2020, 12, 31 ) )
RETURN
ADDCOLUMNS (
Days,
"Year Week", YEAR ( [Date] ) & WEEKNUM([Date])
)
Now mark this table as a date table in the "Table Tools" ribbon with the button "Mark as date table"
3. Convert Weeks to dates
For this to work, I have had to create a calculated column in the Order table with the first day of the year first. This can probably be improved upon.
StartYear = DATE(Left(Orders[Year week], 4), 01, 01)
Next the calculated column that we need in the Order table, that identifies the first day of the week. The Variable "DayNoInYear" takes the week number times 7 and substracting 7 to arrive at the first day of the week, returning the nth day of the year. This is then converted to a date with the variable "DateWeek":
Date =
VAR DayNoInYear = RIGHT(Orders[Year week], 2) * 7 - 7
VAR DateWeek = DATEADD(Orders[StartYear].[Date], DayNoInYear, DAY)
RETURN
DateWeek
6. Create calculated column in Date Table with Day ranges
Day ranges =
VAR Today = TODAY()
VAR CheckDate = 'Calendar'[Date] RETURN
SWITCH(TRUE(),
CheckDate - Today <= -90, "90+ days",
CheckDate - Today <= -60 && CheckDate - Today > -90 , "90 days or less",
CheckDate - Today <= -30 && CheckDate - Today > -60 , "60 days or less",
CheckDate - Today <= 0 && CheckDate - Today > -30 , "30 days or less",
"In the future"
)
7. Create measure to identify if an order was placed
Yes - No order =
VAR Yes_No =
IF(
ISBLANK(FIRSTNONBLANK(Orders[Customer id], Orders[Customer id])),
"No",
"Yes"
)
VAR ThirtyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less"
VAR SixtyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "60 days or less"
VAR NinetyDays = SELECTEDVALUE('Calendar'[Day ranges]) = "30 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "60 days or less" || SELECTEDVALUE('Calendar'[Day ranges]) = "90 days or less"
RETURN
SWITCH(TRUE(),
AND(ThirtyDays = TRUE(), Yes_No = "No"), "No",
AND(SixtyDays = TRUE(), Yes_No = "No"), "No",
AND(NinetyDays = TRUE(), Yes_No = "No"), "No",
Yes_No = "No",
"Yes"
)
Steps 8 and 9
Create slicer with the newly created "Day range" column in the Date table and create a table visual with the "Yes - No order" measure as visual-level filter set at "No" as in screenshot attached below
So, I've 2 tables as under
SALES table:
+----+------------+
| ID | SALE_DATE |
+----+------------+
| 1 | 09-21-2021 |
| 2 | 09-21-2021 |
| 3 | 09-21-2021 |
| 2 | 09-21-2021 |
| 3 | 09-21-2021 |
| 1 | 09-21-2021 |
| 5 | 07-22-2021 |
| 6 | 09-21-2021 |
| 9 | 09-21-2021 |
| 7 | 08-21-2021 |
| 8 | 05-21-2021 |
+----+------------+
CUSTOMER Table
+----+
| ID |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+----+
I want to create 2 measures:
1st would be the count of customers that have no sales in the last 2 months, so in this case it would be 2 (8 and 10)
and second measure would give the list of all those customer ID's (8 and 10)
Right now I use this measure to get the list of all ID's that have no sales in last 2 months
show_hide =
VAR current_name = MIN(SALES[ID])
VAR chk_not_in =
IF(
COUNTROWS(
FILTER(
ALL(SALES),
SALES[ID]= current_name && SALES[SALE_DATE])> DATE(YEAR(NOW()),MONTH(NOW())-2, DAY(NOW()))
)
)= 0,
0,
1
)
VAR chk_in =
IF(
COUNTROWS(
FILTER(
ALL(CUSTOMER),
CUSTOMER[ID] = current_name
)
) = 0,
0,
1
)
RETURN IF(chk_in = 1 && chk_not_in = 1, 1, 0)
So every ID with a show_hide of "0" would be the ones that dont have any sales in the last 2 months
I was wondering if there's an easy way to do it and also, I'm not sure how to get the count of all those ID's
First off - I assume your test data was meant to be 2020 instead of 2021 and the ID in the SALES table is a CUSTOMER ID.
I would address this as a measure and a calculated column.
The measure would calculate the customers that have not sold in the past two months. From your data I think you are missing (4) who has not sold anything - bringing the total customers to three (4, 8, 10).
CustomersWithNoSalesIn2Months =
// Work out what date was 2 months ago
VAR twoMonthsAgo = DATE(YEAR(NOW()),MONTH(NOW())-2, DAY(NOW()))
// Count the total distinct customers in the customer table
VAR totalCustomers = CALCULATE(DISTINCTCOUNT(Customer[ID]))
// Count how many distinct customers did have sales in the past 2 months
VAR customersWithSalesInTheLast2Months = CALCULATE(DISTINCTCOUNT(Sales[ID]), Sales[SALE_DATE] > twoMonthsAgo)
// Subtract the customers who did have sales from the total to get the number of customers that did not have sales
RETURN totalCustomers - customersWithSalesInTheLast2Months
The calculated column would be places on the CUSTOMER table and would count how many sales customers had made in the last 2 months.
SalesMadeInTheLast2Months =
VAR MostRecentSale = CALCULATE(MAX(Sales[SALE_DATE]), FILTER(Sales, Customer[ID] = Sales[ID]))
VAR TwoMonthsAgo = DATE(YEAR(NOW()),MONTH(NOW())-2, DAY(NOW()))
RETURN CALCULATE(COUNTROWS(Sales), FILTER(Sales, Sales[SALE_DATE] > TwoMonthsAgo), FILTER(Sales, Sales[ID] = Customer[ID]))
Now you can filter on the Customers table for BLANK sales or use the counts in any other calculation you need. For example, customers 1,2 & 3 have made the most sales in the past 2 months.
I have a function for a cumulative sum as follows:
Actions closed cumulative =
IF(MAX(Dates[Date])<CALCULATE(MIN(Dates[Date]),
FILTER(all(Dates),Dates[PeriodFiscalYear]=[CurrentPeriod1])),
CALCULATE(Actions[Actions closed on time],
FILTER(ALL(Dates),
Dates[Date]<=max(Dates[Date])
)
))
Where CurrentPeriod1 is the period we're in, which returns something like this:
PeriodFiscalYear | Actions | Actions Closed Cumulative
P01-2018/19 | 4 | 608
P02-2018/19 | 19 | 627
P03-2018/19 | 17 | 644
P04-2018/19 | 6 | 650
P05-2018/19 | 7 | 657
So it's basically counting all the actions closed in the table at the moment but I'd like to reset on a certain number of periods, for example 3 periods would be:
PeriodFiscalYear | Actions | Actions Closed Cumulative
P12-2017/18 | 10 |
P13-2017/18 | 10 |
P01-2018/19 | 4 | 24
P02-2018/19 | 19 | 33
P03-2018/19 | 17 | 40
P04-2018/19 | 6 | 42
P05-2018/19 | 7 | 30
I'm struggling to understand how to do it, despite quite a lot of reading. I have a calendar table with dates by 13 periods per year and also pretty much every measure you could think of, month, monthyear, monthperiod etc etc. Any help would be appreciated. Ultimate goal is a moving average over a set number of periods.
Thanks
Assuming your Actions table uses date values and the periods are stored in the Dates table, I suggest first creating an index column for the periods to that they are much easier to work with:
PeriodIndex = 100 * VALUE(MID(Dates[PeriodFiscalYear], 5, 4)) +
VALUE(MID(Dates[PeriodFiscalYear], 2, 2))
These index values should be integers that look like 201804 instead of P04-2018/19, for example.
Now that you have the index column to work with, you can write a rolling cumulative sum like this:
Trailing3Periods =
VAR CurrentPeriod = MAX(Dates[PeriodIndex])
RETURN CALCULATE(SUM(Actions[Actions closed on time]),
FILTER(ALL(Dates),
Dates[PeriodIndex] <= CurrentPeriod &&
Dates[PeriodIndex] > CurrentPeriod - 3))
I'm currently working on a DBF file manager and I'm having some problems...
One of the elements that compose the header is the last date that the file was updated.
The problem is: the field format is YYMMDD and it MUST have 3 bytes.
How is it possible to write a date using that format using only 3 bytes? Also, another field represents the type of the file (if it have a memo or not).
The file type in my case is 03h and this field MUST use only 1 byte. I'm pretty confused.
I would hold your data in 3 bytes as
first byte = year
second byte = month
third byte = day
There's plenty of room in each byte for the domain of each field (year, month, day). You could write them in an integer with bit-shifting operations, like this:
int year = 13;
int month = 7;
int day = 26;
int my_date = (year << 16) | (month << 8) | day;
Edit:
What I did in my_date: I basically concatenated the information you need (year, month, day) into a series of bits (8 bits per information field), as an integer. You know an int is 4 bytes. Consider for starters that my_date is 0, that is, all 32 bits are 0. The 4 bytes of it are as follows ("|" denotes concatenation; that's for ease of reading):
my_date = 0 | 0 | 0 | 0
When I write year << 16 I have
year << 16 = 0 | year | 0 | 0
In a similar fashion,
month << 8 = 0 | 0 | month | 0
day = 0 | 0 | 0 | day
When I apply the OR operator on all of them, my_date looks like this:
my_date = 0 | year | month | day
Accessing them:
year = (my_date & 0xFF0000) >> 16;
month = (my_date & 0xFF00) >> 8;
day = my_date & 0xFF;
Edit: how accessing works. We previously had
my_date = 0 | year | month | day
If you do, for example, an AND with 0xFF00, which is 0 | 0 | FF | 0, you get
my_date & 0xFF00 = 0 | 0 | month | 0
Now all you need to do is shift your data back, so
(my_date & 0xFF00) >> 8 = 0 | 0 | 0 | month = month
Hope it's clearer now.
First byte for year: 2000 + YY. Can count from 2000 till 2255
Second byte for month: 1-12
Third byte for day: 1-31