I have about 10000 records in an Excel sheet (although I can import into Access 2010 if easier) and I need to set a value in a separate column if the dates fall between certain planting seasons. Year is irrelevant.
Thus if a date falls between a certain range, the column is filled in with its appropriate planting season.
Spring = 3/16 - 5/15
Summer 5/16-8/15
Fall 8/15-10/31
Everything else is Null
3/20/2015 and 4/16/2013 are both "Spring" in the seasons column
6/28/2011 and 8/1/2015 are both Summer, etc.
Ideas as to how I can do this? As I said I can do it in either Excel or Access, whichever approach is easier.
You can use the DateSerial Function in an Access query. For example your spring season for planting date could be expressed as ...
planting_date BETWEEN DateSerial(Year(planting_date), 3, 16) AND DateSerial(Year(planting_date), 5, 15)
You could use similar patterns in a Switch expression to determine the planting season for each planting_date ...
SELECT
y.planting_date,
Switch
(
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 3, 16) AND DateSerial(Year(y.planting_date), 5, 15), 'spring',
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 5, 16) AND DateSerial(Year(y.planting_date), 8, 15), 'summer',
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 8, 16) AND DateSerial(Year(y.planting_date), 10, 31), 'fall',
True, Null
) AS season
FROM YourTable AS y;
If you want to store those season values in a field named planting_season, use the Switch expression in an UPDATE query ...
UPDATE YourTable AS y
SET y.planting_season =
Switch
(
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 3, 16) AND DateSerial(Year(y.planting_date), 5, 15), 'spring',
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 5, 16) AND DateSerial(Year(y.planting_date), 8, 15), 'summer',
y.planting_date BETWEEN DateSerial(Year(y.planting_date), 8, 16) AND DateSerial(Year(y.planting_date), 10, 31), 'fall',
True, Null
);
This formula assumes the first date is in cell A1. Enter the formula in cell B1 (or another empty cell from row 1):
=CHOOSE(MATCH(A1-DATE(YEAR(A1),1,0),{0;75;136;227;305},1),"","Spring","Summer","Fall","")
Now copy downward as far as needed.
That's it.
Note: this works by calculating the day of the year, for example March 16 is the 75th day of the year. Once the 'day of the year' is calculated, a simple binary match is performed on an array of day numbers that correspond to your planting seasons. Finally, the CHOOSE function is used to translate the MATCH results to the name of the season.
To edit your planting schedule, simply adjust this 'days of the year' array: {0;75;136;227;305}
Note: this is an extremely efficient self contained formula. No external helper columns are required and there are no IF functions, nested or otherwise.
Addendum: here is a variation that has a clause that deals with Leap Year:
=CHOOSE(MATCH(A1-DATE(YEAR(A1),1,0),{0;75;136;227;305}+(2=MONTH(DATE(YEAR(A1),2,29))),1),"","Spring","Summer","Fall","")
A few of helper columns to extract month and date would be useful (Assume values are at A1:A1000):
B1 = MONTH(A1)
C1 = DAY(A1)
D1 = DATE(2000, B1, C1)
We intentionally "cheat" about the year for easier comparison.
After that, there's nothing special to do here, just a lot of nested ifs:
IF(AND(D1 >= DATE(2000, 3, 16), D1 <= DATE(2000, 5, 15)), "Spring",
IF(AND(D1 >= DATE(2000, 5, 16), D1 <= DATE(2000, 8, 15)), "Summer",
IF(AND(D1 >= DATE(2000, 8, 16), D1 <= DATE(2000, 10, 31)), "Fall", "Winter")))
Related
I need two excel formulas: for V level and for R level.
Based on the product of numbers a and b, numbers 1-25 are obtained, which are classified into 5 levels (1-5).
The first formula should return the V level of numbers 1-5 based on the product of classified numbers 1-25.
The second formula should return the R level of the numbers I, II, III, IV, V along with the corresponding color.
I tried with the excel formulas: VLOOKUP, INDEX and MATCH, and some other combinations but it doesn't work.
Edit: What I've tried so far:
=INDEX(D18:D22;MATCH(C5*D5;H18:H22;0))
=VLOOKUP(C5:D5;D18:H22;5;FALSE)
=IF(ISNUMBER(MATCH(C5*D5;D18:D22;0));VLOOKUP(C5*D5;D18:H22;5;FALSE))
First option:
Bojan, you can solve this like below with index and vlookup.
In order to use vlookup, first you need to spread your legend table like I did in below screenshot.
C9: =INDEX(D3:H7,B4,E1)
C10: =VLOOKUP(C9,$C$12:$D$25,2,FALSE)
H9: =INDEX(D3:H7,C10,H1)
H10: =VLOOKUP(H9,$C$12:$E$25,3,FALSE)
Orange cells need your manual input.
Second option (after your comment):
After adding below built-in function into a module of your excel file, you will not need the legend table for calculation. Just call your built-in function to get your levels.
Function get_level(levelType As String, value As Integer) As String
If levelType = "V" Then
Select Case value
Case 1, 2
get_level = "1"
Case 3, 4, 5
get_level = "2"
Case 6, 8, 9
get_level = "3"
Case 10, 12, 15, 16
get_level = "4"
Case 20, 25
get_level = "5"
End Select
ElseIf levelType = "R" Then
Select Case value
Case 1, 2
get_level = "I"
Case 3, 4, 5
get_level = "II"
Case 6, 8, 9
get_level = "III"
Case 10, 12, 15, 16
get_level = "IV"
Case 20, 25
get_level = "V"
End Select
End If
End Function
I have a 4D array containing daily time-series of gridded data for different years with shape (year, day, x-coordinate, y-coordinate). The actual shape of my array is (19, 133, 288, 620), so I have 19 years of data with 133 days per year over a 288 x 620 grid. I want to take the weekly average of each grid cell over the period of record. The shape of the weekly averaged array should be (19, 19, 288, 620), or (year, week, x-coordinate, y-coordinate). I would like to use numpy to achieve this.
Here I construct some dummy data to work with and an array of what the solution should be:
import numpy as np
a1 = np.arange(1, 10).reshape(3, 3)
a1days = np.repeat(a1[np.newaxis, ...], 7, axis=0)
b1 = np.arange(10, 19).reshape(3, 3)
b1days = np.repeat(b1[np.newaxis, ...], 7, axis=0)
c1year = np.concatenate((a1days, b1days), axis=0)
a2 = np.arange(19, 28).reshape(3, 3)
a2days = np.repeat(a2[np.newaxis, ...], 7, axis=0)
b2 = np.arange(29, 38).reshape(3, 3)
b2days = np.repeat(b2[np.newaxis, ...], 7, axis=0)
c2year = np.concatenate((a2days, b2days), axis=0)
dummy_data = np.concatenate((c1year, c2year), axis=0).reshape(2, 14, 3, 3)
solution = np.concatenate((a1, b1, a2, b2), axis=0).reshape(2, 2, 3, 3)
The shape of the dummy_data is (2, 14, 3, 3). Per the dummy data, I have two years of data, 14 days per year, over a 3 X 3 grid. I want to return the weekly average of the grid for both years, resulting in a solution with shape (2, 2, 3, 3).
You can reshape and take mean:
week_mean = dummy_data.reshape(2,-1,7,3,3).mean(axis=2)
# in your case .reshape(year, -1, 7, x_coord, y_coord)
# check:
(dummy_data.reshape(2,2,7,3,3).mean(axis=2) == solution).all()
# True
I have a formula for calculating rankings of certain events across different organizations:
=AVERAGEIFS(C2:C100, B2:B100, "A", C2:C100, ">0") * IF(COUNTIF(B2:B100, "A") < 3, 0.7, IF(COUNTIF(B2:B100, "A") < 10, 0.9, IF(COUNTIF(B2:B100, "A") > 30, 1.1, IF(COUNTIF(B2:B100, "A") > 50, 1.2, 1))))
This formula works, but for some reason, using data that I have, I know the AVERAGEIFS value for "A" should be multiplied by 1.2, as there are more than 50 instances of "A" in Col. B. The result the above formula gives multiplies by 1.1.
I've also tried to expand the above formula to incorporate additional factors that could affect the ranking:
=AVERAGEIFS(C2:C100, B2:B100, "A", C2:C100, ">0") * IF(COUNTIF(B2:B100, "A") < 3, 0.7, IF(COUNTIF(B2:B100, "A") < 10, 0.9, IF(COUNTIF(B2:B100, "A") > 30, 1.1, IF(COUNTIF(B2:B100, "A") > 50, 1.2, 1)))) * IF((AND(B2:B100, "A"), COUNTIF(S2:S100, "yes")>1), 1.1, 1))
Running this second formula gives me an error message: "The formula you typed contains an error."
Not sure what's going wrong in the second formula, as I've continued to build on the first formula above.
Since 30 is less than 50 your formula (which short-circuits) never gets as far as what is more than 30 as a separate condition. Maybe something like:
=AVERAGEIFS(C2:C100, B2:B100, "A", C2:C100, ">0") * IF(COUNTIF(B2:B100, "A") < 3, 0.7, IF(COUNTIF(B2:B100, "A") < 10, 0.9, IF(COUNTIF(B2:B100, "A") > 50, 1.2,1))))
This relies in the default factor of 1 for values of 10 to under 30.
Might be easier to see what is happening with:
LOOKUP(COUNTIF(B2:B100,"A"),{0,3,10,30,50},{0.7,0.9,1,1.1,1.2})
after the asterisk.
Problem
Let v = {v1, v2, v3,.., vn} be a set of unsorted real numbers.
I want to count to number of values vi that fall in the intervals [0,10], [20,28] etc.
Example
Let v = {2.6, 3.1, 7, 10, 22, 21, 27}
The number of values that fall in the interval [0,10] are: 2.6,3.1,7,10
The number of values that fall in the interval [20, 28] are : 22, 21 ,27
I used the following formula: =SUMPRODUCT((range>0)*(range<10))
Change > and < to >= and <= in your SUMPRODUCT. You can also try COUNTIFS formula - should work faster. With the following sample layout:
Enter in C3 and drag it down:
=COUNTIFS($A$1:$G$1,">=" &A3,$A$1:$G$1,"<="&B3)
I have excel data in following format.
Date Amount
03-Jan-13 430.00
25-Jan-13 96.00
10-Jan-13 440.00
28-Feb-13 72.10
28-Feb-13 72.30
I need to sum the amount field only if the month lies in Jan Month.
What i have tried is ,
=SUMIF(A2:A6,"MONTH(A2:A6)=1",B2:B6)
But it returns,
0
What i need is,
Following values to be summed, 430.00 + 96.00 + 440.00 = 966.00
Try this instead:
=SUM(IF(MONTH($A$2:$A$6)=1,$B$2:$B$6,0))
It's an array formula, so you will need to enter it with the Control-Shift-Enter key combination.
Here's how the formula works.
MONTH($A$2:$A$6) creates an array of numeric values of the month for the dates in A2:A6, that is, {1, 1, 1, 2, 2}.
Then the comparison {1, 1, 1, 2, 2}= 1 produces the array {TRUE, TRUE, TRUE, FALSE, FALSE}, which comprises the condition for the IF statement.
The IF statement then returns an array of values, with {430, 96, 400.. for the values of the sum ranges where the month value equals 1 and ..0,0} where the month value does not equal 1.
That array {430, 96, 400, 0, 0} is then summed to get the answer you are looking for.
This is essentially equivalent to what the SUMIF and SUMIFs functions do. However, neither of those functions support the kind of calculation you tried to include in the conditional.
It's also possible to drop the IF completely. Since TRUE and FALSE can also be treated as 1 and 0, this formula--=SUM((MONTH($A$2:$A$6)=1)*$B$2:$B$6)--also works.
Heads up: This does not work in Google Spreadsheets
=Sumifs(B:B,A:A,">=1/1/2013",A:A,"<=1/31/2013")
The beauty of this formula is you can add more data to columns A and B and it will just recalculate.
=SUMPRODUCT( (MONTH($A$2:$A$6)=1) * ($B$2:$B$6) )
Explanation:
(MONTH($A$2:$A$6)=1) creates an array of 1 and 0, it's 1 when the
month is january, thus in your example the returned array would be [1, 1, 1, 0, 0]
SUMPRODUCT first multiplies each value of the array created in the above step with values of the array ($B$2:$B$6), then it sums them. Hence in
your example it does this: (1 * 430) + (1 * 96) + (1 * 440) + (0 * 72.10) + (0 * 72.30)
This works also in OpenOffice and Google Spreadsheets