How to compute number of pandas.DataOffset inside another pandas.DataOffset - python

Imagine I have o1=pdandas.tseries.frequencies.to_offset('M') (a MonthEnd object) and o2=pdandas.tseries.frequencies.to_offset('Y') (a YearEnd object).
How to compute how many o1s there are inside 1 o2?
So in my example, I should get the number 12, which means there are 12 MonthEnd (M) periods in 1 YearEnd (Y or A) period.
Between Day (D) and Week (W) I should get the number 7.
Between QuarterEnd (Q) and YearEnd (Y or A) I should get the number 4.
And so on...

The easiest is for fixed frequencies. Here you can simply use .nanos:
>>> m = pd.tseries.frequencies.to_offset('5min')
>>> n = pd.tseries.frequencies.to_offset('1h')
>>> n.nanos / m.nanos
12.0
With non-fixed frequencies, this throws an error:
>>> y = pd.tseries.frequencies.to_offset('1Y')
>>> y.nanos
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pandas/_libs/tslibs/offsets.pyx", line 690, in pandas._libs.tslibs.offsets.BaseOffset.nanos.__get__
ValueError: <YearEnd: month=12> is a non-fixed frequency
You can always use a specific date to find out what this frequency can convert to as a timedelta:
>>> d = pd.Timestamp(0)
>>> (d + y) - d
Timedelta('364 days 00:00:00')
>>> ((d + y) - d).delta
31449600000000000
Now of course dividing these nanoseconds obtained that way is not going to give you exact results − by definition of the frequencies being non fixed. However it should be close enough to round:
>>> yns = ((d + y) - d).delta
>>> qns = ((d + q) - d).delta
>>> q = pd.tseries.frequencies.to_offset('1Q')
>>> yns / qns
4.089887640449438
>>> round(yns / qns)
4
Then you could of course verify that this rounded result holds for your chosen start date:
>>> d + q * 4 == d + y
True
Of course the choice of the date your use for your computations is important, 4 quarters don’t always match 1 year if you don’t start in january:
>>> d = pd.Timestamp('1970-4-01')
>>> d + q * 4 == d + y
False
So that’s something to keep in mind if you use for example frequencies of multiple years.
def div_offsets(a, b, date=pd.Timestamp(0)):
''' Compute pandas dateoffset ratios using nanosecond conversion
'''
try:
return a.nanos / b.nanos
except ValueError:
pass
ans = ((date + a) - date).delta
bns = ((date + b) - date).delta
if ans > bns:
ratio = round(ans / bns)
assert date + ratio * b == date + a
return ratio
else:
ratio = round(bns / ans)
assert date + b == date + ratio * a
return 1 / ratio

Can't think of a straighforward way, but you could try leveraging pandas.date_range:
def get_ratio(offset_a, offset_b, rounding=False):
START = pd.Timestamp.today() # any random day
LARGER_OFFSET = offset_a if START+offset_a > START+offset_b else offset_b
freq_a = pd.date_range(START-LARGER_OFFSET, START+LARGER_OFFSET,
freq=offset_a, closed='left').size
freq_b = pd.date_range(START-LARGER_OFFSET, START+LARGER_OFFSET,
freq=offset_b, closed='left').size
return freq_a / freq_b
offset = pd.tseries.frequencies.to_offset
>>> get_ratio(offset('M'), offset('Y'))
12
>>> get_ratio(offset('Q'), offset('Y'))
4
>>> get_ratio(offset('D'), offset('W'))
7

Related

Trying to use Zeller’s formula to return days of the week using python

I see java codes but can't fully compare them with python. I am trying to use the Zeller’s Congruence to find the day of the week for any date.
Zeller’s formula assumes that for each computation within the formula where there is a
quotient (or a division of two numbers), the integer value of that computation is used. If the month is January or February then 12 is added to the month and 1
subtracted from year before calculating day.
day = (((13*m+3) / 5 + d + y + (y / 4) - (y / 100) + (y / 400)) %7).
day_names =[('Monday'),'Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
for example day_names[0] = 'Monday' and day_names[6] = 'Sunday'
The below does not seem to ever give me the correct dates, is there anyone who might be able to tell what I am doing wrong if not everything?
def day_of_week1(d, m, y):
d=1<=d<=31
m=1<=m<=12
y=1<=y<=10**5
if m in range (1,2):
m=m+12
y=y-1
day_names =[('Monday'),'Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday']
day =(((13*m+3) / 5 + d + y + (y / 4) - (y / 100) + (y / 400)) %7)
day=(math.ceil(day))
if 2>day>=1:
return day_names[0]
elif 3>day>=2:
return day_names[1]
elif 4>day>=3:
return day_names[2]
elif 5>day>=4:
return day_names[3]
elif 6>day>=5:
return day_names[4]
elif 7>day>=6:
return day_names[5]
elif 8>day>=7:
return day_names[6]
Use the day names from the calendar module. Also, the adjustment for the month is not compliant with Zeller's Rule. Try this:
import calendar
def day_of_week(d, m, y):
if m < 3:
y -= 1
m += 10
else:
m -= 2
yc, yd = divmod(y, 100)
r = d + (13 * m - 1) // 5 + yd + yd // 4 + yc // 4 - 2 * yc
return calendar.day_name[r % 7 - 1]
print(day_of_week(9, 11, 1951))
Output:
Friday

Print the value of the series x = 1 + 1/2 + 1/3 + 1/4 + … + 1/n for the user’s input of n

Python 2 how to do this.Print the value of the series x = 1 + 1/2 + 1/3 + 1/4 + … + 1/n for the user’s input of n.
Here you go:
n = int( input() ) # reading user input
x = 0
for i in range(1, n + 1): # adding 1/1 + 1/2 + 1/3 + ... + 1/n
x += 1.0/i
print(x) # => outputs : 2.283333333333333
There may be a Harmonic Series function in Python packages like math or numpy, or some similar way to deal with it, especially if you need high precision at large values of n. Otherwise, you could just do this:
>>> n = 5
>>> print(sum(1.0/i for i in range(1,n+1)))
2.28333333333
Note that the "1.0" is important for Python 2.x so that it knows to deal with floats. Otherwise things get rounded along the way:
>>> print(sum(1/i for i in range(1,n+1)))
1

Calculating Sun Angle Code

My goal with this code was to write a code that measures the degree measure of the sun having that at 6:00 the angle is 0 and at 18:00 the angle is 180 degrees. I tried to make the time input a string and then loop through its characters and pick out the integers and put it into the list that way I could avoid the colon. It seems that this is still a problem. Can someone explain to me what's wrong with this code? Why do I keep getting an "unsupported operand type error"?
def sun_angle(time):
lis = []
time = str(time)
for i in time:
if i.isdigit():
lis.append(i)
else:
continue
a = int(lis[0]*10 + lis[1] + ((lis[2] + lis[3])/60))
b = a - 6
if b < 6 or b > 18:
return "I can't see the sun!"
else:
return b * 15
print(sun_angle("12:12"))
Michael's answer is a great explanation for why what you're doing isn't working (need to convert string to int before manipulating with * and +).
However, there are a lot of ways to parse the time that will be easier to work with than what you're doing here. I'd consider splitting and then parsing the two parts, or you could use the datetime library for more complexity:
# option 1
def parse_time_as_hour(time_str):
hour_str, min_str = time_str.split(':')
return int(hour_str) + int(min_str) / 60.0
# option 2
import datetime
def parse_time_as_hour(time_str):
parsed = datetime.datetime.strptime(time_str, '%H:%M')
return parsed.hour + parsed.minute / 60.0
def sun_angle(time):
fractional_hour = parse_time_as_hour(time)
if fractional_hour < 6 or fractional_hour >= 18:
return "I can't see the sun!"
else:
return (fractional_hour - 6) * 15
If you change the above similar line to:
a = int(lis[0]) * 10 + int(lis[1]) + ((int(lis[2]) + int(lis[3]))/60)
then you get a result. The problem on that line is that you're mixing int and str types. And since you're already passing in a string you can change time = str(time) to time = time. Casting time to a string is redundant.
Your error line is:
a = int(lis[0]*10 + lis[1] + ((lis[2] + lis[3])/60))
since time is a string type
def sun_angle(time):
lis = []
time = str(time)
for i in time:
if i.isdigit():
lis.append(int(i)) #cast it to type int
else:
continue
a = int(lis[0]*10 + lis[1] + ((lis[2] + lis[3])/60))
b = a - 6
if b < 0 or b >= 12:
return "I can't see the sun!"
else:
return b * 15
print(sun_angle("12:12"))
output: 90
You need to cast lis[i] to integer when you're calculating the value of a. 07:00 means sun is up, your logic fails and 18:01 means sun is down.
def sun_angle(time_):
lis = []
time_ = str(time_)
for i in time_:
if i.isdigit():
lis.append(i)
else:
continue
a = int(lis[0])*10
a += int(lis[1])
bb = (int(lis[2])*10 + int(lis[3]))
#print a
#print bb
#b = a - 6
if (a < 6 or a > 18) or (a == 18 and bb > 0):
return "I can't see the sun!"
else:
return (float(a)-6.0) * 15.0 + (15.0*float(bb))/60.0

Solving equation in Python 3.3

So to figure out the day of the week from any date since 15 of october you can use a simple piece of arithmetic my problem is that i have read from a file the date (for e.g. 2009-06-12) and i have put the equation in:
w = (d + [2.6 * m - 0.2] + Y + [Y / 4] + 5 * C + [C / 4] ) % 7
The dates are in the format yyyy-mm-dd and my code looks like this:
count = 5
f = open('/Users/student/Desktop/Harry.txt').readlines()[count]
Y = f[2:4]
C = f[:2]
m = f[5:7]
d = f[8:10]
w = (d + [2.6 * m - 0.2] + Y + [Y / 4] + 5 * C + [C / 4] ) % 7
if w == 0:
print (f, "is a Sunday")
elif w == 1:
print (f, "is a Monday")
elif w == 2:
print (f, "is a Tuesday")
elif w == 3:
print (f, "is a Wednesday")
elif w == 4:
print (f, "is a Thursday")
elif w == 5:
print (f, "is a Friday")
elif w == 6:
print (f, "is a Saturday")
to clarify:
w = day of the week counting from Sunday = 0 Monday = 1
d = the day of the month (for e.g. 28th 13th)
m = month number where March = 1 etc.
Y = last 2 digits of year
C = first 2 digits of year
Yet i get this error
Traceback (most recent call last):
File "/Users/student/Documents/workspace/Tutorial Challenges/src/Day_Of_The_Week.py", line 7, in <module>
w = (d + [2.6 * m - 0.2] + Y + [Y / 4] + 5 * C + [C / 4] ) % 7
TypeError: can't multiply sequence by non-int of type 'float'
Help would be greatly appreciated.
Y, C, m, and d are all strings. You want to convert them into ints first:
Y = int(f[2:4])
C = int(f[:2])
...
Are you sure that equation even works, though? It looks like it'd produce a lot of non-integer weekdays. You might've miscopied it. Also, brackets aren't a grouping operator in Python; they're the list construction syntax. You'll want to replace those brackets with parentheses in the expression for w. (Or were those brackets supposed to be the floor operator? If so, you'll want math.floor, from the math module, or just int if truncation is fine.)
As the above user has said, you want to convert from string to int. However, this is the ideal place to use the datetime module. You can use this to specify that your datetime is of the format: yyyy-mm-dd, and use the module to load up the info as datetime.
http://docs.python.org/2/library/datetime.html

leading number groups between two numbers

(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:
range(2169800, 2171194)
leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X,
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X,
2171191X, 2171192X, 2171193X, 2171194X
or like this:
range(1000, 1452)
leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X,
144X, 1450, 1451, 1452
Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)
def leading(a, b):
# generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
zip_digits = zip(str(a), str(b))
zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
# this ignores problems where the last matching digits are 0 and 9
# leading (12000, 12999) is same as leading(12, 12)
while(zip_digits[-1] == (0,9)):
zip_digits.pop()
# start recursion
return compute_leading(zip_digits)
def compute_leading(zip_digits):
if(len(zip_digits) == 1): # 1 digit case is simple!! :)
(a,b) = zip_digits.pop()
return range(a, b+1)
#now we partition the problem
# given leading(123,456) we decompose this into 3 problems
# lows -> leading(123,129)
# middle -> leading(130,449) which we can recurse to leading(13,44)
# highs -> leading(450,456)
last_digits = zip_digits.pop()
low_prefix = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10 # base for lows e.g. 120
high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10 # base for highs e.g. 450
lows = range(low_prefix + last_digits[0], low_prefix + 10)
highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
#check for boundary cases where lows or highs have all ten digits
(a,b) = zip_digits.pop() # pop last digits of middle so they can be adjusted
if len(lows) == 10:
lows = []
else:
a = a + 1
if len(highs) == 10:
highs = []
else:
b = b - 1
zip_digits.append((a,b)) # push back last digits of middle after adjustments
return lows + compute_leading(zip_digits) + highs # and recurse - woohoo!!
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1452)
def foo(start, end):
index = 0
is_lower = False
while index < len(start):
if is_lower and start[index] == '0':
break
if not is_lower and start[index] < end[index]:
first_lower = index
is_lower = True
index += 1
return index-1, first_lower
start = '2169800'
end = '2171194'
result = []
while int(start) < int(end):
index, first_lower = foo(start, end)
range_end = index > first_lower and 10 or int(end[first_lower])
for x in range(int(start[index]), range_end):
result.append(start[:index] + str(x) + 'X'*(len(start)-index-1))
if range_end == 10:
start = str(int(start[:index])+1)+'0'+start[index+1:]
else:
start = start[:index] + str(range_end) + start[index+1:]
result.append(end)
print "Leading numbers:"
print result
I test the examples you've given, it is right. Hope this will help you
This should give you a good starting point :
def leading(start, end):
leading = []
hundreds = start // 100
while (end - hundreds * 100) > 100:
i = hundreds * 100
leading.append(range(i,i+100))
hundreds += 1
c = hundreds * 100
tens = 1
while (end - c - tens * 10) > 10:
i = c + tens * 10
leading.append(range(i, i + 10))
tens += 1
c += tens * 10
ones = 1
while (end - c - ones) > 0:
i = c + ones
leading.append(i)
ones += 1
leading.append(end)
return leading
Ok, the whole could be one loop-level deeper. But I thought it might be clearer this way. Hope, this helps you...
Update :
Now I see what you want. Furthermore, maria's code doesn't seem to be working for me. (Sorry...)
So please consider the following code :
def leading(start, end):
depth = 2
while 10 ** depth > end : depth -=1
leading = []
const = 0
coeff = start // 10 ** depth
while depth >= 0:
while (end - const - coeff * 10 ** depth) >= 10 ** depth:
leading.append(str(const / 10 ** depth + coeff) + "X" * depth)
coeff += 1
const += coeff * 10 ** depth
coeff = 0
depth -= 1
leading.append(end)
return leading
print leading(199,411)
print leading(2169800, 2171194)
print leading(1000, 1453)
print leading(1,12)
Now, let me try to explain the approach here.
The algorithm will try to find "end" starting from value "start" and check whether "end" is in the next 10^2 (which is 100 in this case). If it fails, it will make a leap of 10^2 until it succeeds. When it succeeds it will go one depth level lower. That is, it will make leaps one order of magnitude smaller. And loop that way until the depth is equal to zero (= leaps of 10^0 = 1). The algorithm stops when it reaches the "end" value.
You may also notice that I have the implemented the wrapping loop I mentioned so it is now possible to define the starting depth (or leap size) in a variable.
The first while loop makes sure the first leap does not overshoot the "end" value.
If you have any questions, just feel free to ask.

Categories

Resources