【问题标题】:Ranking CSV numbers in both ascending and descending orders in python [closed]在python中按升序和降序排列CSV数字[关闭]
【发布时间】:2014-01-08 01:39:38
【问题描述】:

我很惊讶我在 python 中找不到任何关于排名数字的信息...

基本上,我需要两个脚本来执行相同的任务,一个按升序排列,一个按降序排列。

row[2] 是要排名的数字,row[4] 是要放入排名的单元格。

row[0] + row[1] 定义了每个数据集/组

在第一个示例中,数字越大,排名越高。

CSV 示例 1(排名下降)

uniquedata1,uniquecell1,42,data,1,data
uniquedata1,uniquecell1,32,data,2,data
uniquedata1,uniquecell1,13,data,3,data
uniquedata2,uniquecell2,41,data,2,data
uniquedata2,uniquecell2,39,data,3,data
uniquedata2,uniquecell2,45,data,1,data
uniquedata2,uniquecell2,22,data,4,data
uniquedata1,uniquecell2,36,data,3,data
uniquedata1,uniquecell2,66,data,1,data
uniquedata1,uniquecell2,40,data,2,data

在第二个示例中,较大的数字具有较低的排名。

CSV 示例 2(排名上升)

uniquedata1,uniquecell1,42,data,3,data
uniquedata1,uniquecell1,32,data,2,data
uniquedata1,uniquecell1,13,data,1,data
uniquedata2,uniquecell2,41,data,3,data
uniquedata2,uniquecell2,39,data,2,data
uniquedata2,uniquecell2,45,data,4,data
uniquedata2,uniquecell2,22,data,1,data
uniquedata1,uniquecell2,36,data,1,data
uniquedata1,uniquecell2,66,data,3,data
uniquedata1,uniquecell2,40,data,2,data

在第三个例子中,它包含了应该被赋予最高等级的空单元格(如果有两个空白,它们应该被赋予相同的等级)

CSV 示例 3(包括空单元格)

uniquedata1,uniquecell1,42,data,2,data
uniquedata1,uniquecell1,,data,3,data
uniquedata1,uniquecell1,13,data,1,data
uniquedata2,uniquecell2,41,data,3,data
uniquedata2,uniquecell2,,data,3,data
uniquedata2,uniquecell2,,data,3,data
uniquedata2,uniquecell2,22,data,1,data
uniquedata1,uniquecell2,36,data,1,data
uniquedata1,uniquecell2,66,data,3,data
uniquedata1,uniquecell2,40,data,2,data

有人知道我怎样才能达到预期的效果吗?

【问题讨论】:

  • sort csv by column 的可能重复项
  • 您需要想出具有相似含义的替代词并搜索它们 - 例如。 sort 而不是排名。
  • @MrE 问题是我不确定如何进行分组部分,我也不知道如何为空格设置例外。
  • 如果您使用排序这个词而不是排名,您会发现大量信息,包括Python sorting howto。对数字进行排序是微不足道的,包括反向排序。空格排序 (None) 将 None 放在任何数字之前,因此正常排序顺序已经完全满足您的要求。您尚未展示任何您自己解决此问题的努力。
  • 我不明白你的例子。您说第一个排名靠后,而 row[2] 有要排名的数字。如果这是真的,列表中的数据行是否不会按它们的第三列排序(或者可能按它们在行 [4] 中的假定排名)?我还想知道,如果你自己的声望分数甚至没有那么高,你将如何奖励 +50 分。

标签: python sorting python-2.7 pandas ranking


【解决方案1】:

Python 程序员通常使用列表对数据进行排序。编写自己的代码有几个障碍。

  • 内存限制
  • 速度
  • 读取文件并写入新文件
  • 以正确的顺序应用多个排序操作

或者,您可以将数据存储在 sqlite 数据库(基于简单文件的数据库)中,并使用 SQL 查询使用 sqlite3 提取数据。对于某些人来说,这可以说要容易得多,在某些情况下甚至可能是首选。

向我们展示您是如何尝试实现您的结果的,也许我们可以提供进一步的帮助。

【讨论】:

  • 内存限制和速度不是问题,因为文件大小只有 5mb。我不明白集合库的分组是如何工作的,我也不知道如何使用排序功能对空白进行排名......
  • 我们希望看到您的代码 :),这里的人们喜欢修复接近并显示努力的代码。如果您不能提供代码,您应该阅读排序列表上的文档。您可以提供一个函数来替换默认行为,就像许多其他语言允许您做的那样(通过子类或其他方式)。
【解决方案2】:
import sys

#Read the input file
input_data = [line.rstrip().split(",") for line in open("input.txt", 'r').readlines()]

#Put the value and index of each line into a dict,
#categorizing by the dataset/group name. 
#Each different dataset/group is a key of the dict,
#and each key's value is a list.
group_dict = {}
index = 0
for line in input_data:
    group_key = line[0]+","+line[1]
    if group_key not in group_dict.keys():
        group_dict[group_key] = []
    group_dict[group_key].append([index, line[2], None])
    index += 1

#Sort each list of the dict by the numbers.
#Make blank to be a very large number. 
for key in group_dict.keys():
    group_dict[key] = sorted(group_dict[key], key=lambda x: sys.maxint if x[1]=="" else int(x[1]))
    #####group_dict[key] = group_dict[key][::-1]
    ##### Uncomment the above line to sort in descending order  

#Check if there're multiple items with the same number, 
#If so, set them by the same rank.
    group_dict[key][0][2] = 1
    for i in range(1, len(group_dict[key])):
        group_dict[key][i][2] = (group_dict[key][i-1][2] if group_dict[key][i][1] == group_dict[key][i-1][1] else i+1)

#In order to keep the same line order with the input file, 
#get all the lists together into a new list, 
#and sort them by the line index (recorded when put them into the dict).
rank_list = []
for rank in group_dict.values():
    rank_list += rank
rank_list = sorted(rank_list, key=lambda x: x[0])
for rank in rank_list:
    input_data[rank[0]][4] = str(rank[2])

#Output the final list.
for line in input_data:
    print ",".join(line)

测试:

输入:

uniquedata1,uniquecell1,123,data,99,data
uniquedata1,uniquecell1,,data,99,data
uniquedata1,uniquecell1,111,data,99,data
uniquedata2,uniquecell2,456,data,99,data
uniquedata2,uniquecell2,,data,99,data
uniquedata2,uniquecell2,,data,99,data
uniquedata2,uniquecell2,789,data,99,data
uniquedata1,uniquecell2,386,data,99,data
uniquedata1,uniquecell2,512,data,99,data
uniquedata1,uniquecell2,486,data,99,data

输出:

uniquedata1,uniquecell1,123,data,2,data
uniquedata1,uniquecell1,,data,3,data
uniquedata1,uniquecell1,111,data,1,data
uniquedata2,uniquecell2,456,data,1,data
uniquedata2,uniquecell2,,data,3,data
uniquedata2,uniquecell2,,data,3,data
uniquedata2,uniquecell2,789,data,2,data
uniquedata1,uniquecell2,386,data,1,data
uniquedata1,uniquecell2,512,data,3,data
uniquedata1,uniquecell2,486,data,2,data  

【讨论】:

    【解决方案3】:

    如果唯一的区别是排名是按升序还是降序进行,那么您真的不需要两个脚本来完成任务——只需将其作为函数的参数,如图所示。 StrCount 类是如此琐碎,可能不值得付出努力(但我把它留在了里面)。

    import csv
    from itertools import count, groupby
    import sys
    
    _MIN_INT, _MAX_INT = -sys.maxint-1, sys.maxint
    RANK_DOWN, RANK_UP = False, True # larger numbers to get higher or lower rank
    
    class StrCount(count):
        """ Like itertools.count iterator but supplies string values. """
        def next(self):
            return str(super(StrCount, self).next())
    
    def rerank(filename, direction):
        with open(filename, 'rb') as inf:
            reader = csv.reader(inf)
            subst = _MIN_INT if direction else _MAX_INT  # subst value for empty cells
            for dataset, rows in groupby(reader, key=lambda row: row[:2]):
                ranking = StrCount(1)
                prev = last_rank = None
                for row in sorted(rows,
                                  key=lambda row: int(row[2]) if row[2] else subst,
                                  reverse=direction):
                    row[4] = (ranking.next() if row[2] or not row[2] and prev != ''
                                             else last_rank)
                    print ','.join(row)
                    prev, last_rank  = row[2], row[4]
    
    if __name__ == '__main__':
        print 'CSV example_1.csv (ranked down):'
        rerank('example_1.csv', RANK_DOWN)
        print '\nCSV example_2.csv (ranked up):'
        rerank('example_2.csv', RANK_UP)
        print '\nCSV example_3.csv (ranked up):'
        rerank('example_3.csv', RANK_UP)
    

    输出:

    CSV example_1.csv (ranked down):
    uniquedata1,uniquecell1,13,data,1,data
    uniquedata1,uniquecell1,32,data,2,data
    uniquedata1,uniquecell1,42,data,3,data
    uniquedata2,uniquecell2,22,data,1,data
    uniquedata2,uniquecell2,39,data,2,data
    uniquedata2,uniquecell2,41,data,3,data
    uniquedata2,uniquecell2,45,data,4,data
    uniquedata1,uniquecell2,36,data,1,data
    uniquedata1,uniquecell2,40,data,2,data
    uniquedata1,uniquecell2,66,data,3,data
    
    CSV example_2.csv (ranked up):
    uniquedata1,uniquecell1,42,data,1,data
    uniquedata1,uniquecell1,32,data,2,data
    uniquedata1,uniquecell1,13,data,3,data
    uniquedata2,uniquecell2,45,data,1,data
    uniquedata2,uniquecell2,41,data,2,data
    uniquedata2,uniquecell2,39,data,3,data
    uniquedata2,uniquecell2,22,data,4,data
    uniquedata1,uniquecell2,66,data,1,data
    uniquedata1,uniquecell2,40,data,2,data
    uniquedata1,uniquecell2,36,data,3,data
    
    CSV example_3.csv (ranked up):
    uniquedata1,uniquecell1,42,data,1,data
    uniquedata1,uniquecell1,13,data,2,data
    uniquedata1,uniquecell1,,data,3,data
    uniquedata2,uniquecell2,41,data,1,data
    uniquedata2,uniquecell2,22,data,2,data
    uniquedata2,uniquecell2,,data,3,data
    uniquedata2,uniquecell2,,data,3,data
    uniquedata1,uniquecell2,66,data,1,data
    uniquedata1,uniquecell2,40,data,2,data
    uniquedata1,uniquecell2,36,data,3,data
    

    【讨论】:

      【解决方案4】:

      如果你使用 pandas,这很容易。

      import pandas as pd
      
      def sorted_df(df, ascending=False):
          grouped = df.groupby([0,1])
          data = []
          for g in grouped:
              d = g[1]
              d[4] = d[2].rank(ascending=ascending)
              d = d.sort(4)
              data.append(d)
          return pd.concat(data)
      
      # load our dataframe from a csv string
      import StringIO
      f = StringIO.StringIO("""uniquedata1,uniquecell1,42,data,1,data
      uniquedata1,uniquecell1,32,data,2,data
      uniquedata1,uniquecell1,13,data,3,data
      uniquedata2,uniquecell2,41,data,2,data
      uniquedata2,uniquecell2,39,data,3,data
      uniquedata2,uniquecell2,45,data,1,data
      uniquedata2,uniquecell2,22,data,4,data
      uniquedata1,uniquecell2,36,data,3,data
      uniquedata1,uniquecell2,66,data,1,data
      uniquedata1,uniquecell2,40,data,2,data""")
      
      df = pd.read_csv(f, header=None)
      # sort descending
      sorted_df(df)
      =>           0            1   2     3  4     5
      0  uniquedata1  uniquecell1  42  data  1  data
      1  uniquedata1  uniquecell1  32  data  2  data
      2  uniquedata1  uniquecell1  13  data  3  data
      8  uniquedata1  uniquecell2  66  data  1  data
      9  uniquedata1  uniquecell2  40  data  2  data
      7  uniquedata1  uniquecell2  36  data  3  data
      5  uniquedata2  uniquecell2  45  data  1  data
      3  uniquedata2  uniquecell2  41  data  2  data
      4  uniquedata2  uniquecell2  39  data  3  data
      6  uniquedata2  uniquecell2  22  data  4  data
      # sort ascending
      sorted_df(df, ascending=True)
      =>           0            1   2     3  4     5
      2  uniquedata1  uniquecell1  13  data  1  data
      1  uniquedata1  uniquecell1  32  data  2  data
      0  uniquedata1  uniquecell1  42  data  3  data
      7  uniquedata1  uniquecell2  36  data  1  data
      9  uniquedata1  uniquecell2  40  data  2  data
      8  uniquedata1  uniquecell2  66  data  3  data
      6  uniquedata2  uniquecell2  22  data  1  data
      4  uniquedata2  uniquecell2  39  data  2  data
      3  uniquedata2  uniquecell2  41  data  3  data
      5  uniquedata2  uniquecell2  45  data  4  data
      # add some NA values
      from numpy import nan
      df.ix[1,2] = nan
      df.ix[4,2] = nan
      df.ix[5,2] = nan
      # sort ascending
      sorted_df(df, ascending=True)
      =>           0            1   2     3   4     5
      2  uniquedata1  uniquecell1  13  data   1  data
      0  uniquedata1  uniquecell1  42  data   2  data
      1  uniquedata1  uniquecell1 NaN  data NaN  data
      7  uniquedata1  uniquecell2  36  data   1  data
      9  uniquedata1  uniquecell2  40  data   2  data
      8  uniquedata1  uniquecell2  66  data   3  data
      6  uniquedata2  uniquecell2  22  data   1  data
      3  uniquedata2  uniquecell2  41  data   2  data
      4  uniquedata2  uniquecell2 NaN  data NaN  data
      5  uniquedata2  uniquecell2 NaN  data NaN  data
      

      我认为我在这里展示的用于处理 NA 值的行为(将它们列为 NA)可能比您在假设示例中展示的行为更合适,但您可以在每个组中使用您想要的任何内容填充 NA 值fillna.

      【讨论】:

      • 在找到pandas.pydata.org/pandas-docs/dev/generated/… 之后,我正在开发一个版本,但你的版本比我的尝试要好得多,我把它做得很hacky。当然我可以用 NaN 设置 NA 单元格(来自 numpy?)
      • 是的,这正是我在这里所做的。查看以from numpy import nan 开头的四行。
      猜你喜欢
      • 1970-01-01
      • 2011-08-25
      • 2015-10-12
      • 2018-11-20
      • 2017-03-21
      • 2011-12-06
      • 2021-12-19
      • 1970-01-01
      • 2020-02-08
      相关资源
      最近更新 更多