【问题标题】:CSV Calculation without any libraries on PythonPython 上没有任何库的 CSV 计算
【发布时间】:2021-11-14 09:57:51
【问题描述】:

我有这样的 CSV 文件:

ID,TASK1,TASK2,QUIZ1,QUIZ2
11061,50,75,50,78
11062,70,80,60,50
11063,60,75,77,79
11064,52,85,50,80
11065,70,85,50,80

如何获得新行 NO 并在 TASK1,TASK2,QUIZ1,QUIZ2 上获得最高和最低分数。并覆盖它?我限制在 Python 上使用任何库,我的 Expect 输出是:

NO,ID,TASK1,TASK2,QUIZ1,QUIZ2
1,11061,50,75,50,78
2,11062,70,80,60,50
3,11063,60,75,77,79
4,11064,52,85,50,80
5,11065,70,85,50,80
MAX, ,70,85,77,80
MIN, ,50,75,50,50

【问题讨论】:

标签: python csv


【解决方案1】:

您可以在r+ 模式下打开文件,这将允许您在不重新打开的情况下读取和写入文件。

简短的(理解起来有点复杂)版本的代码(下面有解释):

with open(r"filename.csv", "r+") as f:
    next(f)  # skip first line
    [*columns] = zip(*((int(i) for i in line.split(",")[1:]) for line in f))
    print("MAX", "", *map(max, columns), sep=",", file=f)
    print("MIN", "", *map(min, columns), sep=",", file=f)

现在让我们解释一下它是如何工作的。为了获得每列的最大值/最小值,我们必须将列的所有值分组到一个容器中。为了得到行,我们应该用逗号分割每一行并将每个值转换为整数。由于我们不需要第一列的值,我们可以跳过它。所有这些工作都由一行代码完成:

[*columns] = zip(*((int(i) for i in line.split(",")[1:]) for line in f))

同样的,我们可以用更明确的方式来写:

rows = []
for line in f:
    row = []
    for column in line.split(",")[1:]:  # skip first column
        row.append(int(column))  # convert string to int
    rows.append(row)
columns = list(zip(*rows))

[*columns] = iterable 与columns = list(iterable) 相同。

在我们得到包含每列值的元组列表后,我们可以将max() 和min() 应用于每个元组以获得最大值和最小值。我们可以使用生成器表达式来做到这一点:

(max(column) for column in columns)
(min(column) for column in columns)

或者使用map():

map(max, columns)
map(min, columns)

要将结果写入文件,我们需要将值转换为字符串并使用 str.join() 将它们连接回单个字符串:

max_row = ["MAX", ""]  # empty string needed to get double comas
min_row = ["MIN", ""]
for column in columns:
    max_row.append(str(max(column)))
    min_row.append(str(min(column)))
f.write("\n" + ",".join(max_row))
f.write("\n" + ",".join(min_row))

或者,我们可以使用print()。我们可以unpack 打印我们的结果,将"," 传递给sep 参数,这将使python 使用逗号连接所有参数,并将我们的文件对象传递给file 参数以使其写入文件而不是控制台。

print("MAX", "", *map(max, columns), sep=",", file=f)
print("MIN", "", *map(min, columns), sep=",", file=f)

所以这是答案顶部的无聊版本的代码:

with open(r"filename.csv", "r+") as f:
    next(f)  # skip first line
    rows = []
    for line in f:
        row = []
        for column in line.split(",")[1:]:  # skip first column
            row.append(int(column))  # convert string to int
        rows.append(row)
    max_row = ["MAX", ""]  # empty string needed to get double comas
    min_row = ["MIN", ""]
    for column in zip(*rows):
        max_row.append(str(max(column)))
        min_row.append(str(min(column)))
    f.write("\n" + ",".join(max_row))
    f.write("\n" + ",".join(min_row))

Upd. 如果要将结果写入单独的文件并添加索引列,您还需要打开一个目标文件进行写入,将源文件的每一行重写到目标(添加它的索引)并将最小值和最大值附加到文件末尾。

修改后的短版代码(变得更加复杂):

with open(r"input.csv") as inp_f, \
        open(r"output.csv", "w") as out_f:
    out_f.write("NO," + next(inp_f)) 
    [*columns] = zip(*(out_f.write(f"{index},{line.rstrip()}\n") and
                       (int(i) for i in line.split(",")[1:])
                       for index, line in enumerate(inp_f)))
    print("MAX", "", *map(max, columns), sep=",", file=out_f)
    print("MIN", "", *map(min, columns), sep=",", file=out_f)

修改无聊版代码:

with open(r"input.csv") as inp_f, \
        open(r"output.csv", "w") as out_f:
    out_f.write("NO," + next(inp_f))
    rows = []
    for index, line in enumerate(inp_f, 1):
        out_f.write(str(index) + "," + line)
        row = []
        for column in line.split(",")[1:]:  # skip first column
            row.append(int(column))  # convert string to int
        rows.append(row)
    max_row = ["MAX", ""]  # empty string needed to get double comas
    min_row = ["MIN", ""]
    for column in zip(*rows):
        max_row.append(str(max(column)))
        min_row.append(str(min(column)))
    out_f.write("\n" + ",".join(max_row))
    out_f.write("\n" + ",".join(min_row))

更新。我做了一些测试(code)。测试结果(越低越好):

olvin1: 0.687356842
olvin2: 0.5448804249999999
nikeros: 0.7540002289999999
balderman: 0.6384034139999999

因此,此答案中的“无聊”方法展示了最佳性能。

【讨论】:

  • 你真的认为答案中的OP能够应付python级别的...?
  • @balderman,这就是为什么我在底部添加了无聊的版本
  • @OlvinR​​oght 哇你真的帮了我先生,非常感谢你!!!
  • @OlvinR​​oght 我如何仅获得 QUIZ1 或 QUIZ2 等的最大值/最小值?
  • @FAISHALYUSUFBAQIR, list(zip(*rows)) 将返回每列的值列表。您可以按索引访问所需的列,例如min(list(zip(*rows))[0]) 将返回 QUIZ1 的最大值。不过,您的任务尚不清楚。也许使用索引max_row = [] ; ... ; max_row[0] 访问已经计算的最小值和最大值就足够了。
【解决方案2】:

见下文 - 零进口

TASK1 = 1
TASK2 = 2
QUIZ1 = 3
QUIZ2 = 4

MIN = 0
MAX = 1
values = [[None,None],[None,None],[None,None],[None,None]]
lines = []
with open('in.txt') as f:
  for idx,line in enumerate(f):
    if idx > 0:
      lines.append(line)
      parts = line.split(',')
      for i in [TASK1,TASK2,QUIZ1,QUIZ2]:
        if values[i-1][MIN] is None or values[i-1][MIN] > parts[i]:
          values[i-1][MIN] = parts[i].strip()    
        if values[i-1][MAX] is None or values[i-1][MAX] < parts[i]:
          values[i-1][MAX] = parts[i].strip()
with open('out.txt','w') as f: 
  f.write('NO,ID,TASK1,TASK2,QUIZ1,QUIZ2\n') 
  for idx,line in enumerate(lines,1):
    f.write(f'{idx},{line}') 
  f.write('\n')  
  f.write('MAX, ,' + ','.join(str(x[MAX]) for x in values)) 
  f.write('\n')  
  f.write('MIN, ,' + ','.join(str(x[MIN]) for x in values)) 

out.txt

NO,ID,TASK1,TASK2,QUIZ1,QUIZ2
1,11061,50,75,50,78
2,11062,70,80,60,50
3,11063,60,75,77,79
4,11064,52,85,50,80
5,11065,70,85,50,80
MAX, ,70,85,77,80
MIN, ,50,75,50,50

【讨论】:

    【解决方案3】:

    假设您的输入已经在 csv 文件中,您可以使用以下代码:

    # This reads in your file
    def load_csv(p):
        with open(p, "r") as f:
            return [l.strip().split(",") for l in f]
    
    # This isolates the numeric columns you are interested in
    def to_numeric(lines):
        return [list(map(int,line[1:])) for i, line in enumerate(lines) if i > 0]
    
    # This applies a function to the columns results of to_numeric
    def apply_function(numeric_lists, func=max):
        return [func(x) for x in zip(*numeric_lists)] 
    
    # This applies the defined function, printing your result in the desired format
    def main(p):
        lines = load_csv(p)
        numeric_lists = to_numeric(lines)
        mins = apply_function(numeric_lists, func=min)
        maxs = apply_function(numeric_lists)
        for i, l in enumerate(lines):
            print(("No," if i == 0 else str(i)) + ",".join(l))
        print(f'MAX, ,{",".join(map(str,maxs))}')
        print(f'MIX, ,{",".join(map(str,mins))}')
    
    # Simply call
    main(YOUR_FILE_PATH)
    

    【讨论】:

      猜你喜欢
      • 2020-02-09
      • 2014-03-03
      • 2022-01-07
      • 1970-01-01
      • 2022-01-23
      • 1970-01-01
      • 1970-01-01
      • 2017-04-03
      • 1970-01-01
      相关资源
      最近更新 更多