导出CSV、Python中的First、Last、Next排序

2024-04-18 09:55:57 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个项目和值的CSV,它的表示形式如下所示:

foo, 569
bar, 9842
asdasd, 98
poiqweu, 7840
oiasd, 4
poeri, 145
sacodiw, 55
aosdwr, 855
9523, 60
a52sd, 5500
sdcw, 415
0932, 317

我想导出到三个CSV,以便它们从主CSV接收项目,顺序是:highest、lowest、next highest、next lowest等

CSV1应该是:

bar, 9842
oiasd, 4
poiqweu, 7840
sacodiw, 55

其他两个CSV也是如此。你知道吗

对于奖金,我真正想做的是从一个270的主数据中创建三个CSV,每个CSV包含90个项目,这样三个CSV中的每一个都尽可能接近相同的值总和。我想有一个比我简单的(高度假设的)方法更好的方法。你知道吗

我将如何在我已经使用的python脚本中实现这一点(包括CSV和pandas,如果后者有帮助的话)?你知道吗


Tags: csv项目方法foobar形式nextasdasd
3条回答

这是一个局部的解决方案

reorder是功能性的,但是由于我不太熟悉pandas,所以我只使用了Python的内置数据结构。你知道吗

编辑:我用贪婪的实现替换了partition_by_sum;它试图找到相等的和,但不注意每个箱子的项数。对更好算法的建议?

这应该给你一个很好的开端。你知道吗

from collections import defaultdict
import csv

VALUE_COL = 1
NUM_BINS = 3

inp = [
    ["foo",      569],
    ["bar",     9842],
    ["asdasd",    98],
    ["poiqweu", 7840],
    ["oiasd",      4],
    ["poeri",    145],
    ["sacodiw",   55],
    ["aosdwr",   855],
    ["9523",      60],
    ["a52sd",   5500],
    ["sdcw",     415],
    ["0932",     317]
]

def load_csv(fname, **kwargs):
    with open(fname, "rb") as inf:
        for row in csv.reader(inf, **kwargs):
            yield row

def save_csv(fname, rows, **kwargs):
    with open(fname, "wb") as outf:
        csv.writer(outf, **kwargs).writerows(rows)

def make_index(lst, col):
    """
    Index a table by column;
    return list of column-values and dict of lists of rows having that value
    """
    values, index = [], defaultdict(list)
    for row in lst:
        val = row[col]
        values.append(val)
        index[val].append(row)
    return values, index

def min_index(lst):
    """
    Return index of min item in lst
    """
    return lst.index(min(lst))

def partition_by_sum(values, num_bins, key=None):
    """
    Try to partition values into lists having equal sum

    Greedy algorithm, per http://en.wikipedia.org/wiki/Partition_problem#Approximation_algorithm_approaches
    """
    values.sort(key=key, reverse=True)   # sort descending
    bins = [[] for i in xrange(num_bins)]
    sums = [0] * num_bins
    for value in values:
        index = min_index(sums)
        bins[index].append(value)
        sums[index] += value
    return bins

def reorder(lst, key=None):
    """
    Return [highest, lowest, second-highest, second-lowest, ...]
    """
    lst.sort(key=key, reverse=True)    # sort in descending order
    halflen = (len(lst) + 1) // 2      # find midpoint
    highs, lows = lst[:halflen], lst[halflen:][::-1]   # grab [high half descending], [low half ascending]
    lst[0::2], lst[1::2] = highs, lows                 # reassemble
    return lst

def main():
    # load data
    data = inp    # load_csv("input_file.csv")

    # solve partitioning    
    values, index = make_index(data, VALUE_COL)
    bins = partition_by_sum(values, NUM_BINS)

    # rearrange for output
    bins = [[index[val].pop() for val in reorder(bin)] for bin in bins]

    # write output
    for i,bin in enumerate(bins, 1):
        save_csv("output_file_{}.csv".format(i), bin)

if __name__=="__main__":
    main()

如果数据有N行,我会采用这种方法:

  • 按降序排列输入数据。你知道吗
  • 创建3个空列表
  • 迭代已排序的数据,并将当前行添加到具有最小和的列表中,除非此列表已经有N/3个或更多条目

在阅读了维基百科上关于the partition problem的页面之后,我发现这个算法是the greedy algorithm的一个改编,唯一的例外是我要求所有子集具有相同的长度(如果N%3==0)。你知道吗

我编写了一个简单的代码片段来向您演示。我认为这比你提出的解决办法更好。从下面的输出中可以看到,第一个数据集包含最高值和3个最低值。你提出的解决方案会让总金额有更大的差异。你知道吗

import csv

class DataSet:
    def __init__(self, filename):
        self.total = 0
        self.data = []
        self.filename = filename

    def add(self, row):
        self.total += int(row[1])
        self.data.append(row)

    def write(self):
        with open(self.filename, 'wb') as ofile:
            writer = csv.writer(ofile)
            writer.writerows(self.data)

with open('my_data.csv') as ifile:
    data = sorted(csv.reader(ifile), key=lambda l: -int(l[1]))

subsets = DataSet('data_1.csv'), DataSet('data_2.csv'), DataSet('data_3.csv')

for row in data:
    sets = [k for k in subsets if len(k.data) < 4]
    min(sets, key=lambda x: x.total).add(row)

for k in subsets:
    print k.data, k.total
    k.write()

输出:

[['bar', ' 9842'], ['9523', ' 60'], ['sacodiw', ' 55'], ['oiasd', ' 4']] 9961
[['poiqweu', ' 7840'], ['0932', ' 317'], ['poeri', ' 145'], ['asdasd', ' 98']] 8400
[['a52sd', ' 5500'], ['aosdwr', ' 855'], ['foo', ' 569'], ['sdcw', ' 415']] 7339

您可以使用以下构建块来解决问题(从这里开始并不难):

使用pandas加载和排序:

import pandas as pd
original = pd.read_csv('test.csv', names=['name','count'])
df_highest_first  = df.sort(columns=['count'])
df_smallest_first = df.sort(columns=['count'], ascending=False)

largest_1 = df_largest['count'][0:-1:2].values
largest_2 = df_largest['count'][1:-2:2].values

smallest_1 = df_smallest['count'][0:-1:2].values
smallest_2 = df_smallest['count'][1:-2:2].values

然后izip在列表对之间交错元素:

result = list(chain.from_iterable(izip(list_a, list_b)))

相关问题 更多 >