I must write a program that accepts a number, n, where -6 < n < 2. The program must print out the numbers n to n+41 as 6 rows of 7 numbers. The first row must contain the values n to n+6, the second, the values n+7 to n+7+6, and so on.
That is, numbers are printed using a field width of 2, and are right-justified. Fields are separated by a single space. There are no spaces after the final field.
Output:
Enter the start number: -2
-2 -1 0 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 32
33 34 35 36 37 38 39
The numbers need to be directly lined under each other.
I have absolutely no idea how to do this
This is my code so far:
start = int(input('Enter the start number: '))
for n in range(n,n+41):
If you could help me I will really appreciate it.
I assume you are not allowed to use a library to tabulate the numbers for you and are expected to do the logic yourself.
You need to print 6 rows of numbers. Start by determining the first number of each row. That is given by range(n,n+42,7) (note, not n+41). For starting value -2, those are the numbers -2, 5, 12, 19, 26, 33. Every other number in the row is just the next 6 integers. If the first number in the row is leftmost then the entire row is given by range(leftmost, leftmost + 7). So the first row those are the numbers -2, -1, 0, 1, 2, 3, 4.
To print 6 rows of 7 numbers you need a loop with 6 iterations, one for each value of leftmost. Inside that loop you print the other numbers. The only complication is all of the numbers in the list must be followed by a space, except the last. So that has to get special treatment.
You need to specify format {0:2d} to ensure that "numbers are printed using a field width of 2".
n = -2
for leftmost in range(n,n+42,7):
for value in range(leftmost,leftmost + 6):
print("{0:2d}".format(value), end=" ")
print("{0:2d}".format(leftmost+6))
-2 -1 0 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 32
33 34 35 36 37 38 39
check the tabulate library here, you can use it to format the output - the tablefmt="plain" parameter produces a very similar table.
If you store the numbers in a list you can use list slicing to get the rows of 7 numbers each and put those in an another list to satisfy the format that tabulate is expecting
from tabulate import tabulate
n = 2
while not -6 < n < 2:
n = int(input('Please submit a number greater than -6 and smaller than 2:\n'))
number_list, output_list = [], []
for i in range(42):
number_list.append(n + i)
for i in range(6):
output_list.append(number_list[i*7:i*7+7])
print()
print(
tabulate(
output_list,
tablefmt='plain'
)
)
Please submit a number greater than -6 and smaller than 2:
-3
-3 -2 -1 0 1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
32 33 34 35 36 37 38
Related
I have a data frame containing three columns, whereas col_1 and col_2 are containing some arbitrary data:
data = {"Height": range(1, 20, 1), "Col_1": range(2, 40, 2), "Col_2": range(3, 60, 3)}
df = pd.DataFrame(data)
Height Col_1 Col_2
0 1 2 3
1 2 4 6
2 3 6 9
3 4 8 12
4 5 10 15
5 6 12 18
6 7 14 21
7 8 16 24
8 9 18 27
9 10 20 30
10 11 22 33
11 12 24 36
12 13 26 39
13 14 28 42
14 15 30 45
15 16 32 48
16 17 34 51
17 18 36 54
18 19 38 57
and another data frame containing height values, that should be used to segment the Height column from the df.
data_segments = {"Section Height" : [1, 10, 20]}
df_segments = pd.DataFrame(data_segments)
Section Height
0 1
1 10
2 20
I want to create two new data frames, df_segment_0 containing all columns of the initial df but only for Height rows within the first two indices in the df_segments. The same approach should be taken for the df_segment_1. They should look like:
df_segment_0
Height Col_1 Col_2
0 1 2 3
1 2 4 6
2 3 6 9
3 4 8 12
4 5 10 15
5 6 12 18
6 7 14 21
7 8 16 24
8 9 18 27
df_segment_1
Height Col_1 Col_2
9 10 20 30
10 11 22 33
11 12 24 36
12 13 26 39
13 14 28 42
14 15 30 45
15 16 32 48
16 17 34 51
17 18 36 54
18 19 38 57
I tried the following code using the .loc method and added the suggestion of C Hecht to create a list of data frames:
df_segment_list = []
try:
for index in df_segments.index:
df_segment = df[["Height", "Col_1", "Col_2"]].loc[(df["Height"] >= df_segments["Section Height"][index]) & (df["Height"] < df_segments["Section Height"][index + 1])]
df_segment_list.append(df_segment)
except KeyError:
pass
Try-except is used only to ignore the error for the last name entry since there is no height for index=2. The data frames in this list can be accessed as C Hecht:
df_segment_0 = df_segment_list[0]
Height Col_1 Col_2
0 1 2 3
1 2 4 6
2 3 6 9
3 4 8 12
4 5 10 15
5 6 12 18
6 7 14 21
7 8 16 24
8 9 18 27
However, I would like to automate the naming of the final data frames. I tried:
for i in range(0, len(df_segment_list)):
name = "df_segment_" + str(i)
name = df_segment_list[i]
I expect that this code to simply automate the df_segment_0 = df_segment_list[0], instead I receive an error name 'df_segment_0' is not defined.
The reason I need separate data frames is that I will perform many subsequent operations using Col_1 and Col_2, so I need row-wise access to each one of them, for example:
df_segment_0 = df_segment_0 .assign(col_3 = df_segment_0 ["Col_1"] / df_segment_0 ["Col_2"])
How do I achieve this?
EDIT 1: Clarified question with the suggestion from C Hecht.
If you want to get all entries that are smaller than the current segment height in your segmentation data frame, here you go :)
import pandas as pd
df1 = pd.DataFrame({"Height": range(1, 20, 1), "Col_1": range(2, 40, 2), "Col_2": range(3, 60, 3)})
df_segments = pd.DataFrame({"Section Height": [1, 10, 20]})
def segment_data_frame(data_frame: pd.DataFrame, segmentation_plan: pd.DataFrame):
df = data_frame.copy() # making a safety copy because we mutate the df !!!
for sh in segmentation_plan["Section Height"]: # sh is the new maximum "Height"
df_new = df[df["Height"] < sh] # select all entries that match the maximum "Height"
df.drop(df_new.index, inplace=True) # remove them from the original DataFrame
yield df_new
# ATTENTION: segment_data_frame() will calculate each segment at runtime!
# So if you don't want to iterate over it but rather have one list to contain
# them all, you must use list(segment_data_frame(...)) or [x for x in segment_data_frame(...)]
for segment in segment_data_frame(df1, df_segments):
print(segment)
print()
print(list(segment_data_frame(df1, df_segments)))
If you want to execute certain steps on those steps you can just use the defined list like so:
for segment in segment_data_frame(df1, df_segments):
do_stuff_with(segment)
If you want to keep track and name the individual frames, you can use a dictionary
Unfortunately I don't 100% understand what you have in mind, but I hope that the following should help you in finding the answer:
import pandas as pd
import numpy as np
df = pd.DataFrame({'Section Height': [20, 90, 111, 232, 252, 3383, 3768, 3826, 3947, 4100], 'df_names': [f'df_section_{i}' for i in range(10)]})
df['shifted'] = df['Section Height'].shift(-1)
new_dfs = []
for index, row in df.iterrows():
if np.isnan(row['shifted']):
# Don't know what you want to do here
pass
else:
new_df = pd.DataFrame({'heights': [i for i in range(int(row['Section Height']), int(row['shifted']))]})
new_df.name = row['df_names']
new_dfs.append(new_df)
The content of new_dfs are dataframes that look like this:
heights
0 20
1 21
2 22
3 23
4 24
.. ...
65 85
66 86
67 87
68 88
69 89
[70 rows x 1 columns]
If you clarify your questions given this input, we could help you all the way, but this should hopefully point you in the right direction.
Edit: A small comment on using df.name: This is not really stable and if you do stuff like dropping a column, pickling/unpickling, etc. the name will likely be lost. But you can surely find a good solution to maintain the name depending on your needs.
I'm supposed to create code that will simulate a d20 sided dice rolling 25 times using np.random.choice.
I tried this:
np.random.choice(20,25)
but this still includes 0's which wouldn't appear on a dice.
How do I account for the 0's?
Use np.arange:
import numpy as np
np.random.seed(42) # for reproducibility
result = np.random.choice(np.arange(1, 21), 50)
print(result)
Output
[ 7 20 15 11 8 7 19 11 11 4 8 3 2 12 6 2 1 12 12 17 10 16 15 15
19 12 20 3 5 19 7 9 7 18 4 14 18 9 2 20 15 7 12 8 15 3 14 17
4 18]
The above code draws numbers from 0 to 20 both inclusive. To understand why, you could check the documentation of np.random.choice, in particular on the first argument:
a : 1-D array-like or int
If an ndarray, a random sample is generated from its elements. If an
int, the random sample is generated as if a was np.arange(n)
np.random.choice() takes as its first argument an array of possible choices (if int is given it works like np.arrange), so you can use list(range(1, 21)) to get the output you want
+1
np.random.choice(20,25) + 1
def alphabet_position(text):
a= range(1,27)
z=""
b=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
o=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
for i in text:
if i in b:
k=b.index(i)
z=z+" "+ str(a[k])
elif i in o:
k=o.index(i)
z=z +" "+ str(a[k])
else:
pass
return(z)
Output:
' 20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11' should equal '20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11'
Input:
test.assert_equals(alphabet_position("The sunset sets at twelve o' clock."), "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11")
Why does 'should equal' appear in between for the output? And why is it repeating?
My previous answer was deleted because it wasn't an answer. By the time I replied there was no question so there was nothing to answer, but now there is:
You are asserting if they are equal. That menas that python will tell you if they are not equal, thats why it says they should be equal. The difference is the startign space.
Also, some code improvements:
1. a[k] is exactly the same than k + 1, no need to store a range, and look in it every time when you can just increment k by 1.
2. Having b and o is redundant, by lower-casing your input you get the desired result.
3. else: pass is not needed.
4. Module strings has a helpful string with all the lowercase letters.
5. return is not a function, do not wrap it in parenthesis.
6. By using " ".join() you will add spaces between the numbers but not at the start.
Thanks to #wjandrea for some of the improvements
from string ascii_lowercase as lowercases
def alphabet_position(text):
z = []
for i in text.lower():
if i in lowercases:
z.append(str(lowercases.index(i) + 1))
return " ".join(z)
Or with a generator instead of a for loop:
from string ascii_lowercase as lowercases
def alphabet_position(text):
return " ".join(str(lowercases.index(i) + 1) for i in text.lower() if i in lowercases)
Iv`e been asked to create the following pyramid in python with only using 2 for loops.
1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
8 16 24 32 40 48 56 64
9 18 27 36 45 54 63 72 81
Is there a shorter way only using 2 for loops as apposed to my program?
for x in range(1, 10):
for y in range(0, 1):
print ('1')
print("2 4")
for u in range(1, 2):
for v in range(0,1):
print("3 6 9")
print("4 8 12 16")
for t in range(1, 2):
for s in range(0, 1):
print("5 10 15 20 25")
print("6 12 18 24 30 36")
for r in range(1, 2):
for q in range(0, 1):
print("7 14 21 28 35 42 49")
print("8 16 24 32 40 48 56 64")
for p in range(0,1):
print("9 18 27 36 45 54 63 72 81")
I'm gonna do this more of a code review...
Your first line for x in range(1,10): shows that you want to create a loop to iterate through a sequence of code 9 times (10 - 1).
With programming, counting starts from 0, instead of saying range(1,10), you can say range(0,9) or even range(9).
This helps make it more readable (I read 9 faster than computing 10 - 1)
Inside this main loop, you've added a lot of for loops... for y in range(1,2):. What this means is that it will iterate through the code indented once, as 2 - 1 is 1. Therefore this for loop is irrelevant and you can just simply use print('1').
If we simplify your code taking into account whats said above, we get:
for x in range(9):
print("1")
print("2 4")
print("3 6 9")
print("4 8 12 16")
print("5 10 15 20 25")
print("6 12 18 24 30 36")
print("7 14 21 28 35 42 49")
print("8 16 24 32 40 48 56 64")
print("9 18 27 36 45 54 63 72 81")
Isn't that already much nicer and more readable. If you type this into python, it will produce the same result as the code you gave, thats because computationally it is the same, however in your method you have unnecessary steps as mentioned above.
Taking this a step further..
Your code prints out this 'pyramid' 9 times. I think you want to print out this pyramid once, so therefore we will have to remove the initial for loop. What you want to for loop is what is printed. Because there is a pattern behind it, a relationship, you can create a for loop to do this for you. This also helps create a more general-case usability. (So instead of doing this until 9, 18, 27, you can do it until n*1, n*2, n*3 [also a checky hint of what you should for loop]).
Hope this was helpful, comment if theres anything you dont understand!
EDIT:
I was just doing some random coding and I found a quite neat way of creating the same result (though it is quite confusing), just wanted to share...
for k in [[(j+1)*i for j in range(i)] for i in range(1,11)]:
print k
for i in range(1,10):
for j in range (1,i+1):
print(i*j)
This will print every number on a new line though. If you want to keep everything on the same line, and you don't want to print a list or something, then you might add to a string instead:
for i in range(1,10):
x = ''
for j in range(1,i+1)
x=x+str(i*j)+' '
print(x.strip())
Four lines, really:
for x in range(1, 10):
for y in range(x, x ** 2 + 1, x):
print (y, end = ' ')
print('\n')
This makes use of the start, stop and step parameters and will yield:
1
2 4
3 6 9
4 8 12 16
5 10 15 20 25
6 12 18 24 30 36
7 14 21 28 35 42 49
8 16 24 32 40 48 56 64
9 18 27 36 45 54 63 72 81
To get rid of the extra newlines, just change the print('\n') statement to print().
So far I have tried to create a table of the first 6 multiples of 2 that should give:
2 4 6 8 10 12
My code for this currently looks like:
i = 1
while i <= 6:
print(2*i ,' \t' , )
i = i + 1
But this outputs them vertically, not horizontally so:
2
4
6
8
10
12
There is also a tab after each number.
You can use a simple for-loop as they are more Pythonic for this application than while loops.
So just loop through the numbers between 1 and 6 and print that number multiplied by 2 with a space instead of the new-line character. Finally, you can call one more print at the end to move to the next line after they have all been printed.
for i in range(1, 7):
print(i * 2, end=' ')
print()
which outputs:
2 4 6 8 10 12
If you wanted do this whole thing in one-line, you could use a generator and string.join(). You may not fully understand this, but the following produces the same result:
print(" ".join(str(i*2) for i in range(1, 7)))
which gives:
2 4 6 8 10 12
Note that one last thing is that this method doesn't produce a uniformly spaced table. To show what I mean by "uniformly spaced", here are some examples:
not uniformly spaced:
1234 1 123 12 12312 123
uniformly spaced:
1234 1 123 12 12312 123
To make the output print nicely like above, we can use .ljust:
print("".join(str(i*2).ljust(3) for i in range(1, 7)))
which gives a nicer table:
2 4 6 8 10 12
Finally, if you wanted to make this into a function that is more general purpose, you could do:
def tbl(num, amnt):
print("".join(str(i*num).rjust(len(str(num*amnt))+1) for i in range(1,amnt+1)))
and some examples of this function:
>>> tbl(3, 10)
3 6 9 12 15 18 21 24 27 30
>>> tbl(9, 5)
9 18 27 36 45
>>> tbl(10, 10)
10 20 30 40 50 60 70 80 90 100
Change your print statement to be like this: print(2*i, end=' \t') The end key argument sets what the ending character should be. By default, it's a newline, as you know.