Ранжирование номеров CSV как по возрастанию, так и по убыванию в python

Я удивлен, что ничего не могу найти о ранжировании чисел в 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

Во втором примере большие числа имеют меньший ранг.

Пример 2 в формате CSV (повышение ранга)

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

В этом третьем примере, который ранжируется вверх, он включает пустые ячейки, которым должны быть присвоены самые высокие ранги (если есть два пробела, им должен быть присвоен один и тот же ранг).

Пример 3 файла CSV (включая пустые ячейки)

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

Кто-нибудь знает, как я могу достичь желаемого результата?


person Ryflex    schedule 19.12.2013    source источник
comment
возможный дубликат сортировать csv по столбцу   -  person YXD    schedule 19.12.2013
comment
Вам нужно придумать альтернативные слова, которые означают похожие вещи, и также найти их - например, sort, а не ранг.   -  person doctorlove    schedule 19.12.2013
comment
@MrE проблема в том, что я не знаю, как сделать часть группировки, и я также не знаю, как сделать исключения для пробелов.   -  person Ryflex    schedule 19.12.2013
comment
Если бы вы использовали слово сортировка вместо ранжирования, вы бы нашли массу информации, включая Как сортировать Python. Сортировка по числам тривиальна, включая сортировку в обратном порядке. Сортировка с пробелами (None) ставит None перед любыми числами, поэтому обычный порядок сортировки уже точно соответствует вашим ожиданиям. То, что вы не показали, так это любые попытки решить эту проблему самостоятельно.   -  person Martijn Pieters    schedule 21.12.2013
comment
Я не понимаю ваших примеров. Вы говорите, что первый ранжируется вниз, а в строке [2] есть числа, которые нужно ранжировать. Если бы это было правдой, разве строки данных в списке не были бы упорядочены по их третьему столбцу (или, возможно, по их предполагаемому рангу в строках [4])? Я также хотел бы знать, как вы собираетесь присуждать награду в размере +50 очков, если ваша собственная оценка репутации даже не так высока.   -  person martineau    schedule 21.12.2013
comment
@martineau row[2] — это столбец данных, которые необходимо ранжировать, а row[4] — это вывод ранга. Наборы данных или группы определяются путем объединения row[0] и row[1] вместе, что в этом примере дает нам 3 уникальных класса для индивидуального ранжирования. Моя репутация раньше была 87, я назначил награду и теперь она 37. Я все еще могу присудить награду.   -  person Ryflex    schedule 22.12.2013
comment
@MartijnPieters Я осмотрелся и сначала не мог ничего найти, пока кто-то не упомянул в питоне, что это называется сортировкой, которая по сравнению с тем, к чему я привык, имеет совершенно другое значение, и после этого я понятия не имею, как использовать collections правильно, и я не знаю, как ранжировать (сортировать) в моей ситуации.   -  person Ryflex    schedule 22.12.2013
comment
В ПОРЯДКЕ. В ваших примерах row[4] уже имеет значение (ранг) - так отображаемые строки данных являются входными или желаемыми выходными данными? Если это ввод, то значение следует игнорировать и пересчитывать на основе row[2] и группы, верно?   -  person martineau    schedule 22.12.2013
comment
Говоря более кратко: что именно является вводом? Являются ли показанные примеры предполагаемыми результатами (которые представляют собой CSV-файлы)?   -  person martineau    schedule 22.12.2013
comment
@martineau да, row[4] уже имеет желаемый ранг / значение, все, что там должно быть перезаписано новым расчетом, потому что размер группы может получить новые записи или меньше записей.   -  person Ryflex    schedule 22.12.2013
comment
@Hyflex, если есть четыре числа 10,20,20,21, то какими должны быть их ранги? 1,2,3,4, 1,2,2,4 или 1,2,2,3?   -  person Skyler    schedule 22.12.2013
comment
@Hyflex, и должен ли порядок строк вывода быть таким же, как и ввод?   -  person Skyler    schedule 22.12.2013
comment
@Skyler На основании чисел, которые вы мне дали, он вернет ранги 1,2,2,4 в отношении порядка строк, да, они должны оставаться в том же порядке.   -  person Ryflex    schedule 22.12.2013


Ответы (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.

person mattexx    schedule 22.12.2013
comment
Я работал над версией после того, как нашел pandas.pydata .org/pandas-docs/dev/generated/, но ваша попытка намного лучше, чем моя попытка, я сделал ее довольно хакерской. Конечно, я могу установить ячейки NA с помощью NaN (из numpy?) - person Ryflex; 23.12.2013
comment
Да, это именно то, что я сделал здесь. Обратите внимание на четыре строки, начинающиеся с from numpy import nan. - person mattexx; 23.12.2013

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  
person Skyler    schedule 22.12.2013

Вам действительно не нужны два сценария для задачи, если единственная разница заключается в том, должно ли ранжирование выполняться по возрастанию или по убыванию - просто сделайте его аргументом функции, как показано. Класс 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
person martineau    schedule 22.12.2013

Программисты Python обычно используют список для сортировки данных. Есть несколько препятствий для написания собственного.

  • Ограничения памяти
  • Скорость
  • Чтение файла и запись нового файла
  • Применение нескольких операций сортировки в правильном порядке

В качестве альтернативы вы можете хранить данные в базе данных sqlite (простая база данных на основе файлов) и использовать запрос SQL для извлечения данные с помощью sqlite3. Это, возможно, намного проще для некоторых людей, а в некоторых случаях даже может быть предпочтительнее.

Покажите нам, как вы пытались достичь своего результата, и, возможно, мы сможем помочь в дальнейшем.

person Derek Litz    schedule 19.12.2013
comment
Ограничения памяти и скорость не являются проблемой, так как размер файлов составляет всего 5 МБ. Я не понимаю, как работает группировка, когда речь идет о библиотеке коллекций, и не могу понять, как использовать функцию сортировки для ранжирования пробелов... - person Ryflex; 19.12.2013
comment
Мы хотим увидеть ваш код :), людям здесь нравится исправлять код, который приближается и показывает усилия. Если вы не можете предоставить код, вам следует прочитать документы по сортировке списков. Вы можете предоставить функцию для замены поведения по умолчанию, как это позволяют делать многие другие языки (через подклассы или другие средства). - person Derek Litz; 19.12.2013