import os
import requests


def download_sheet(url):
    """only download file if not already present.
    """
    file_name = os.path.basename(url)
    if os.path.exists(file_name):
        return file_name  
    data = requests.get(url).text
    with open(file_name, "w") as fh:
        fh.write(data)
    return file_name
        
        
file_name = download_sheet("https://siscourses.ethz.ch/python_challenges/data_grouped.csv")
def pretty_print(sheet, max_col_width=8):
    for row in sheet:
        for cell in row:
            cell = str(cell)[:max_col_width]
            cell = (max_col_width - len(cell)) * " " + cell
            print(cell, end=" ")
        print()
import csv

def read_sheet(file_name):
    with open(file_name, "r") as fh:
        reader = csv.reader(fh, delimiter=",")
        result = []
        for line in reader:
            result.append(line)
        return result
    
sheet = read_sheet(file_name)

# check first 10 rows
pretty_print(sheet[:10])
  group1   -32.69   -17.72   -30.14    42.63    29.27    38.42 
  group6    25.37     5.31      -49   -10.44     41.8   niente     0.62            -27.86   -15.14   -39.06 
  group7    41.12   -14.84   -30.12   niente     0.89    14.73   -46.05 
  group1    49.63   -34.04   niente     0.61 
  group1      4.8    12.93 
  group4   -28.31    -7.95    -5.41    -48.5     6.04   -41.59    25.79            -41.87    45.85        8 
  group8     21.4    48.23    47.99     2.63     48.1   -24.46    -7.17             42.37    14.76   -24.18 
  group6    46.01   niente     0.33    32.99     7.88    18.11   -38.27            -23.03   niente     0.45 
  group9     6.31   -48.11    -8.45    46.44    22.75   -38.56    -5.71             18.33   -23.05   -32.08 
  group6    30.65     3.95   -12.66    22.24     4.38   niente     0.01 
def to_float(txt):
    try:
        return float(txt)
    except ValueError:
        return None
    
print(to_float("niente"), to_float("1.23"))
None 1.23
def fix_invalid_numbers(sheet):
    """converts invalid numbers in number cells to None,
    """
    result = []
    for row in sheet:
        new_row = [row[0]]
        for i, cell in enumerate(row[1:]):
            new_row.append(to_float(cell))
        result.append(new_row)    
    return result
  
fixed = fix_invalid_numbers(sheet)
# check first 10 rows
pretty_print(fixed[:10])    
  group1   -32.69   -17.72   -30.14    42.63    29.27    38.42 
  group6    25.37     5.31    -49.0   -10.44     41.8     None     0.62     None   -27.86   -15.14   -39.06 
  group7    41.12   -14.84   -30.12     None     0.89    14.73   -46.05 
  group1    49.63   -34.04     None     0.61 
  group1      4.8    12.93 
  group4   -28.31    -7.95    -5.41    -48.5     6.04   -41.59    25.79     None   -41.87    45.85      8.0 
  group8     21.4    48.23    47.99     2.63     48.1   -24.46    -7.17     None    42.37    14.76   -24.18 
  group6    46.01     None     0.33    32.99     7.88    18.11   -38.27     None   -23.03     None     0.45 
  group9     6.31   -48.11    -8.45    46.44    22.75   -38.56    -5.71     None    18.33   -23.05   -32.08 
  group6    30.65     3.95   -12.66    22.24     4.38     None     0.01 
def extend_rows(sheet):
    """extends rows with None values so that all rows have the same length 
    afterwards. 
    """
    result = []
    max_length = max([len(row) for row in sheet])
    for row in sheet:
        missing = max_length - len(row)
        fill_up = [None] * missing
        new_row = row + fill_up
        result.append(new_row)
    return result
              
fixed = extend_rows(fixed)

# check first 10 rows
pretty_print(fixed[:10])
  group1   -32.69   -17.72   -30.14    42.63    29.27    38.42     None     None     None     None     None 
  group6    25.37     5.31    -49.0   -10.44     41.8     None     0.62     None   -27.86   -15.14   -39.06 
  group7    41.12   -14.84   -30.12     None     0.89    14.73   -46.05     None     None     None     None 
  group1    49.63   -34.04     None     0.61     None     None     None     None     None     None     None 
  group1      4.8    12.93     None     None     None     None     None     None     None     None     None 
  group4   -28.31    -7.95    -5.41    -48.5     6.04   -41.59    25.79     None   -41.87    45.85      8.0 
  group8     21.4    48.23    47.99     2.63     48.1   -24.46    -7.17     None    42.37    14.76   -24.18 
  group6    46.01     None     0.33    32.99     7.88    18.11   -38.27     None   -23.03     None     0.45 
  group9     6.31   -48.11    -8.45    46.44    22.75   -38.56    -5.71     None    18.33   -23.05   -32.08 
  group6    30.65     3.95   -12.66    22.24     4.38     None     0.01     None     None     None     None 
def compute_average(values):
    values = [v for v in values if v is not None]
    if not values:     # empty list is handled as False
        return None
    return sum(values) / len(values)

print(compute_average([]))
print(compute_average([None]))
print(compute_average([1, None, 3]))
print(compute_average([1, 3, 2]))
None
None
2.0
2.0
def strip_first_column(sheet):
    """removes first comlum"""
    return [row[1:] for row in sheet]

stripped = strip_first_column(fixed)
# check
pretty_print(stripped[:5])
  -32.69   -17.72   -30.14    42.63    29.27    38.42     None     None     None     None     None 
   25.37     5.31    -49.0   -10.44     41.8     None     0.62     None   -27.86   -15.14   -39.06 
   41.12   -14.84   -30.12     None     0.89    14.73   -46.05     None     None     None     None 
   49.63   -34.04     None     0.61     None     None     None     None     None     None     None 
     4.8    12.93     None     None     None     None     None     None     None     None     None 
def column_wise_averages(sheet):
    """computes column wise average of given cells,
    cells is a list of lists"""
    
    n0 = len(sheet[0])
    column_averages = []
    for column_index in range(n0):
        column_values = [row[column_index] for row in sheet]
        average = compute_average(column_values)
        column_averages.append(average)
        
    return column_averages


averages = column_wise_averages(stripped)
print(averages)
[3.2153333333333327, -4.317096774193548, -4.188064516129033, 5.540689655172414, -0.612666666666667, -0.5458620689655165, 5.756296296296296, None, -2.4635, 7.82888888888889, -8.241666666666667]

Grouped averages

from collections import defaultdict

def split_sheet(sheet):
    sheets = defaultdict(list)
    for row in sheet:
        group_id = row[0]
        data = row[1:]
        sheets[group_id].append(data)
        
    return sheets
    
sheets = split_sheet(fixed)
print("avail groups:", sheets.keys())
print()
print("sheet of group1:")
pretty_print(sheets["group1"])
avail groups: dict_keys(['group1', 'group6', 'group7', 'group4', 'group8', 'group9', 'group5', 'group3', 'group0', 'group2', 'group11'])

sheet of group1:
  -32.69   -17.72   -30.14    42.63    29.27    38.42     None     None     None     None     None 
   49.63   -34.04     None     0.61     None     None     None     None     None     None     None 
     4.8    12.93     None     None     None     None     None     None     None     None     None 
   44.83    13.93    45.74     None     0.23   -31.25      6.2     None     None    20.49    -5.58 
  -47.73    21.18   -41.08    19.02    19.02     29.2    32.78     None     None     None     None 
result_sheet = []
for group, sheet in sheets.items():
    averages = column_wise_averages(sheet)
    row = [group] + averages
    result_sheet.append(row)
    
pretty_print(result_sheet)
  group1 3.768000 -0.74399 -8.49333 20.75333 16.17333 12.12333 19.49000     None     None    20.49    -5.58 
  group6  15.7875 3.246666  -14.662   23.044   11.242 3.619999   -8.672     None -28.3833   -15.14 -12.8166 
  group7   5.5625 -13.9533   -17.13 22.35333 2.123333 0.357499 -6.54249     None    32.46  -25.225 0.024999 
  group4   -31.04  -3.6775  -13.625    -18.4 -28.7099    -1.25 32.06333     None -14.2766 42.49333 2.706666 
  group8 -7.96500 5.367499 -2.84000 -9.05666 14.04000     5.71 -1.86999     None 11.51499    7.615   -18.98 
  group9 28.42666 -14.2133 3.247499   18.325 5.995000 -0.79249     1.63     None -2.27000     3.16     5.95 
  group5 2.745000   -15.72 -2.42000 -1.63666 -18.4599 -28.5033 -7.21666     None -9.18666   22.265  -18.775 
  group3 -1.40333    10.46 11.34333  -34.065 -7.42333 3.670000    29.77     None 12.65000 -4.09500   -24.41 
  group0   -20.28    -1.98    20.28    36.91   -48.29   -32.05    22.84     None     None     0.37     None 
  group2    40.16   -44.91    26.17   -20.26     2.45    26.14     None     None   -32.63    -2.88   -30.42 
 group11     None     0.56     None     None     None     None     None     None     None     None     None