3

I am trying to print my nested dictionary in a table format. But have not been able to do so. The table is a list of student names and student grades. I would like it to be in a "Report Card" type format that is easy to read. The for loop I am using prints out the results. But it is one column list form.

This is my current dictionary I have setup.

student = {
    "student1": {
        "Name": "Eddy",
        "Grade": 1,
        "Math": 78,
        "English": 65,
        "Physics": 89,
        "Chemistry": 80
    },
    "student2": {
        "Name": "Jim",
        "Grade": 2,
        "Math": 89,
        "English": 65,
        "Physics": 87,
        "Chemistry": 76

    },
    "student3": {
        "Name": "Jane",
        "Grade": 3,
        "Math": 87,
        "English": 97,
        "Physics": 75,
        "Chemistry": 64

    },
}

This is the for loop I am currently using.

for i in student:
    for j in student[i]:
        print(j + " : " + str(student[i][j]))
    print("===========")

I would like to print out in a table format.

Name Grade Math English Physics Chemistry
Eddy   1    78      65     89        80
Jim    2    89      65     87        76
Jane   3    87      97     75        64

I have tried to use the format() function but could not make it work with the nested dictionary.

I also tried using pandas and converting to a pd.DataFrame, but could not make it work.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
PythonCoder1981
  • 403
  • 9
  • 19
  • it seen you should learn about `string formatting` in python - see page [pyFormat.info](https://pyformat.info/) – furas Jan 03 '21 at 03:00
  • `print()` automatically add new line - so you may need `print(..., end="")` to print in one line. – furas Jan 03 '21 at 03:07

6 Answers6

4

First: print() as default add new line at the end so if you want to display in one line then you have to use print(..., end="")


I think you should learn string formatting - see page pyFormat.info. This way you can better control elements in row then using spaces or \t.

It needs to work with all elements at once instead of using internal for-loop but this gives better control and needs simpler code.

for stud, val in student.items():
    print("{:4} {:^5} {:^4} {:^7} {:^7} {:^9}".format(val["Name"], val["Grade"], val["Math"], val["English"], val["Physics"], val["Chemistry"]))

I use items() to get all values for one studen and later use val["Name"], val["Grade"}, etc. so I'm sure I put values in correct order.

I use {} to define where to put element in string, and using {:^5} I define that it should use field which has 5 chars (it will add spaces if text is shorter) and ^ means it should center value in this field.

Name Grade Math English Physics Chemistry
Eddy   1    78    65      89       80    
Jim    2    89    65      87       76    
Jane   3    87    97      75       64 

In string formatting you can use {} with any other text to decoreate table. For example you can put | between fields to create

+------+-------+------+---------+---------+-----------+
| Name | Grade | Math | English | Physics | Chemistry |
+------+-------+------+---------+---------+-----------+
| Eddy |   1   |  78  |   65    |   89    |    80     |
| Jim  |   2   |  89  |   65    |   87    |    76     |
| Jane |   3   |  87  |   97    |   75    |    64     |
+------+-------+------+---------+---------+-----------+

student = {
    "student1": {
        "Name": "Eddy",
        "Grade": 1,
        "Math": 78,
        "English": 65,
        "Physics": 89,
        "Chemistry": 80
    },
    "student2": {
        "Name": "Jim",
        "Grade": 2,
        "Math": 89,
        "English": 65,
        "Physics": 87,
        "Chemistry": 76

    },
    "student3": {
        "Name": "Jane",
        "Grade": 3,
        "Math": 87,
        "English": 97,
        "Physics": 75,
        "Chemistry": 64

    },
}

print('Name Grade Math English Physics Chemistry')

for stud, val in student.items():
    print("{:4} {:^5} {:^4} {:^7} {:^7} {:^9}".format(val["Name"], val["Grade"], val["Math"], val["English"], val["Physics"], val["Chemistry"]))

print('+------+-------+------+---------+---------+-----------+')
print('| Name | Grade | Math | English | Physics | Chemistry |')
print('+------+-------+------+---------+---------+-----------+')

for stud, val in student.items():
    print("| {:4} | {:^5} | {:^4} | {:^7} | {:^7} | {:^9} |".format(val["Name"], val["Grade"], val["Math"], val["English"], val["Physics"], val["Chemistry"]))

print('+------+-------+------+---------+---------+-----------+')

BTW: to make it more universal you could use list 'Name', 'Grade', 'Math', 'English', 'Physics', 'Chemistry'] to decide which values display. You could also need code which calculate width for columns but it would be more complex code.

furas
  • 134,197
  • 12
  • 106
  • 148
4

Here is a pandas solution, as you mentioned unsuccessfully trying pandas. You can use pandas.DataFrame.from_dict with orient=index.

import pandas as pd

df = pd.DataFrame.from_dict(student, orient='index').reset_index(drop=True)

You get

    Name    Grade   Math    English Physics Chemistry
0   Eddy    1       78      65      89      80
1   Jim     2       89      65      87      76
2   Jane    3       87      97      75      64

For a markdown table

print(df.to_markdown(index=False))

| Name   |   Grade |   Math |   English |   Physics |   Chemistry |
|:-------|--------:|-------:|----------:|----------:|------------:|
| Eddy   |       1 |     78 |        65 |        89 |          80 |
| Jim    |       2 |     89 |        65 |        87 |          76 |
| Jane   |       3 |     87 |        97 |        75 |          64 |
  • The benefit of pandas over other implementations, is the data is now in a format for easy analysis and plotting.
import matplotlib.pyplot as plt

df.plot.barh(x='Name', y=['Math', 'English', 'Physics', 'Chemistry'], figsize=(6, 8))
plt.xlabel('Score')
plt.legend(title='Subject', bbox_to_anchor=(1.05, 1), loc='upper left')

enter image description here

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Vaishali
  • 37,545
  • 5
  • 58
  • 86
3

just happened to write one yesterday. sharing it.

student = {
    "student1": {
        "Name": "Eddy",
        "Grade": 1,
        "Math": 78,
        "English": 65,
        "Physics": 89,
        "Chemistry": 80
    },
    "student2": {
        "Name": "Jim",
        "Grade": 2,
        "Math": 89,
        "English": 65,
        "Physics": 87,
        "Chemistry": 76

    },
    "student3": {
        "Name": "Jane",
        "Grade": 3,
        "Math": 87,
        "English": 97,
        "Physics": 75,
        "Chemistry": 64

    },
}

columns = list(student.values())[0].keys()
width = [len(col) for col in columns ]
print(" ".join(columns))
col_format = []
for w in width:
    if len(col_format) == 0:
        col_format.append("%s")
    else:
        col_format.append("%" + str(w) + "d")
str_col_format = " ".join(col_format)

for vals in student.values():
    values = list(vals.values())
    values[0] = f"{values[0].ljust(width[0])}"
    print(str_col_format % tuple(values))
D. Seah
  • 4,472
  • 1
  • 12
  • 20
1

Probably not the most elegant way, but it does work.

print('\t'.join(['Name', 'Grade', 'Math', 'English', 'Physics', 'Chemistry']))
for stud in student:
    for topic in student[stud]:
        print(student[stud][topic], end='\t')
    print()
Jacob Lee
  • 4,405
  • 2
  • 16
  • 37
1
def get_keys(my_dict):
    keys = list(my_dict.keys())
    keys = my_dict[keys[0]].keys()
    keys = list(keys)
    return keys

# get headers
keys = get_keys(students)

# print header
for key in keys:
    print("%10s " %key, end="")
print("\n")

# print body
for values in students.values():
    for key in keys:
        print("%10s "%str(values[key]), end="")
    print("\n")

(this will not necessarily print Name as the first column though)

Amir
  • 307
  • 3
  • 14
1

You can pass in .values() of the dict to pandas.DataFrame

import pandas as pd

student = ...

df = pd.DataFrame(student.values())
print(df.to_string(index=False))
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
ywbaek
  • 2,971
  • 3
  • 9
  • 28