Сортировка столбцов csv в bash, чтение вывода bash в переменные python - PullRequest
4 голосов
/ 16 октября 2010

Привет У меня есть тонна данных в нескольких файлах CSV и отфильтровать набор данных с помощью grep:

user@machine:~/$ cat data.csv | grep -a "63[789]\...;"
637.05;1450.2
637.32;1448.7
637.60;1447.7
637.87;1451.5
638.14;1454.2
638.41;1448.6
638.69;1445.8
638.96;1440.0
639.23;1431.9
639.50;1428.8
639.77;1427.3

Я хочу выяснить набор данных, который имеет наибольшее количество, справа от столбца;а затем узнать соответствующее значение (слева от;).В этом случае набор, который я ищу, будет 638.14; 1454.2

Я пробовал разные вещи и в итоге использовал комбинацию bash и python, которая работает, но не очень красиво:

os.system('ls | grep csv > filelist')
files = open("filelist")
files = files.read()
files = files.split("\n")

for filename in files[0:-1]:
  os.system('cat ' + filename + ' | grep -a "63[6789]\...;" > filtered.csv')
  filtered = csv.reader(open('filtered.csv'), delimiter=';')
  sortedlist = sorted(filtered_file, key=operator.itemgetter(1), reverse=True)
  dataset = sortedlist[0][0] + ';' + sortedlist[0][1] + '\n'

Я бы хотел иметь решение только для bash (cut, awk, arrays?!?), Но не смог бы его понять.Также мне не нравится работа вокруг записи команд bash в файлы и последующего чтения их в переменные python.Могу ли я прочитать их непосредственно в переменные или есть лучшие решения этой проблемы?(вероятно, Perl и т. д. ... но я действительно заинтересован в решении Bash ..)

Большое спасибо!

Ответы [ 7 ]

6 голосов
/ 16 октября 2010

Быстрая однострочная строка будет выглядеть так:

grep -a "63[789]\...;" data.csv | sort -n -r -t ';' -k 2 | head --lines=1

Это просто сортирует файл численно по второму столбцу, а затем печатает первую строку.Надеюсь, это поможет.

3 голосов
/ 16 октября 2010

Если вы собираетесь использовать Python, то используйте Python. Почему вы смешиваете команды bash вместе? Это делает ваш код не переносимым / зависимым от среды bash.

import os
import glob
import operator
os.chdir("/mypath")
for file in glob.glob("*.csv"):
    data=open(file).readlines()
    data=[i.strip().split(";") for i in data if i[:3] in ["637","638","639"]]
    # data=[i.strip().split(";") for i in data if i[:3] in ["637","638","639"] and isinstance(float(i[:6]),float) ]
    sortedlist = sorted(data, key=operator.itemgetter(1), reverse=True)
    print "Highest for file %s: %s" % (file,sortedlist[0])

ИЛИ, если вас больше интересует решение bash + tools

find . -type f -name '*.csv' |while read -r FILE
do
 grep -a "63[789]\...;" "$FILE" | sort -n -r -t ';' -k 2 | head -1  >> output.txt
done
1 голос
/ 16 октября 2010

Вот код, который я написал для сортировки CSV-файлов с использованием Python.Он позволяет указывать несколько столбцов и сортировать их в обратном порядке, используя знак минус.

#!/usr/bin/env python
# Usage:
# (1) sort ctb_consolidated_test_id.csv by Academic Year, Test ID, Period, and Test Name, with Test ID in descending order
#   sort_csv.py -c "Academic Year" -c "-Test ID" -c "Period" -c "Test Name" ctb_consolidated_test_id.csv
from __future__ import with_statement
from __future__ import print_function

import sys

def multikeysort(items, columns):
    from operator import itemgetter
    import re
    num_re = re.compile(r'^\d+$')
    comparers = [
        ((itemgetter(col[1:].strip()), -1) if col.startswith('-') else (itemgetter(col.strip()), 1))
        for col in columns
    ]
    def number_comparable(val1, val2):
        return len(val1) != len(val2) and num_re.match(val1) and num_re.match(val2)
    def column_comparer(left, right):
        for fn, mult in comparers:
            val1, val2 = fn(left), fn(right)
            if number_comparable(val1, val2):
                val1, val2 = int(val1), int(val2)
            result = cmp(val1, val2)
            if result:
                return mult * result
        return 0
    return sorted(items, cmp=column_comparer)

def sort_csv(filename, columns):
    import csv
    with open(filename, "r") as f:
        reader = csv.DictReader(f)
        writer = csv.DictWriter(sys.stdout, reader.fieldnames)
        writer.writerow(dict(zip(reader.fieldnames, reader.fieldnames)))
        writer.writerows(multikeysort(reader, columns))

if __name__ == '__main__':
    from glob import glob
    from optparse import OptionParser, make_option
    option_list = [
        make_option('-c', '--column', dest='columns', action='append', metavar='COLUMN NAME'),
    ]
    parser = OptionParser(option_list=option_list)
    (options, args) = parser.parse_args()
    filenames = (filename for arg in args for filename in glob(arg))
    for filename in filenames:
        sort_csv(filename, options.columns)
1 голос
/ 16 октября 2010

Если у вас есть тонна данных , то вы не хотите сохранять все эти данные в памяти, а затем сортировать , чтобы получить максимальное значение,Этот подход неэффективен в отношении сложности вычислений и памяти.

Вместо этого вы можете просто анализировать файлы и вычислять нужные значения на лету. быстрый чистый подход Python для решения вашей проблемы:

import os, re
os.chdir('/path/to/csvdir')
for f in os.listdir('.'):
    dataset, count = 0.0, 0.0
    for line in open(f):
        if re.search(r'63[6789]\...', line):
            d, c = map(float, line.strip().split(';'))
            if count < c:
                dataset, count = d, c
    print f, dataset

Этот подход также можно использовать для отображения списка максимальных значений (если может быть несколько наборов данных с самым высокимрассчитывает) путем изменения соответствующих строк:

dataset, count = [], 0.0
...
        if count < c:
            dataset, count = [d], c
        elif count == c:
            dataset.append(d)

Редактировать: скрипт предполагает, что ваш csvdir заполнен только файлами, содержащими формат синтаксического анализа.Если вы хотите отфильтровать их по имени, вы можете использовать либо glob (с ограниченными возможностями регулярного выражения при фильтрации имен):

for f in glob.glob('*.csv'):

или применить фильтр к os.listdir:

for f in filter(lambda f: re.match('.*\.csv', f), os.listdir('.')):
1 голос
/ 16 октября 2010
$ cat data.csv | grep -a "63[789]\...;" | awk 'BEGIN {FS=";"} $2>max{max=$2; val=$1} END {print "max " max " at " val}' 

max 1454.2 at 638.14
0 голосов
/ 16 октября 2010

Я знаю, что вы ищете решение на основе bash, но я не мог не предложить что-то, используя модуль csv .

import os
import csv
import re

target_re = re.compile(r'^63[789]\.\d\d$')
csv_filenames = [f for f in os.listdir('.') if f.endwith('.csv')]
largest_in_each_file = []

for f in csv_filenames:
    largest = (None, 0)
    for a,b in csv.reader(open(f, 'rb'), delimiter=';'):
        if target_re.match(a) and b > largest[1]:
            largest = (a, b)
    largest_in_each_file.append(largest)


largest_overall = largest_in_each_file[0]
for largest in largest_in_each_file:
    print "%s;%s in %s" % largest
    if largest[1] > largest_overall[1]:
        largest_overall = largest

print "-" * 10
print "%s;%s in %s is the largest record in all files" % largest_overall
0 голосов
/ 16 октября 2010

хорошо, спасибо большое, Акоп Палян !!

Теперь есть хитрость, как получить эти данные из всех файлов CSV и собрать их где-нибудь как новый файл? что-то вроде

 find . -name '*.csv' -print0 | xargs -0 grep -a "63[789]\...;" | sort -n -r -t ';' -k 2 | head --lines=1

этот выводит только первую строку, мне нужно было бы перебрать отдельные файлы и собрать наборы данных ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...