Справка по средним табличным данным Python - PullRequest
0 голосов
/ 22 сентября 2010

Хорошо, у меня есть следующая рабочая программа. Он открывает файл данных в столбцах, который слишком велик для Excel, и находит среднее значение для каждого столбца:

Пример данных:

Joe Sam Bob
1   2   3
2   1   3

И он возвращается

Joe Sam Bob
1.5 1.5 3

Это хорошо. Проблема в том, что некоторые столбцы имеют значение NA. Я хочу пропустить этот NA и вычислить среднее из оставшихся значений Итак

Bobby
1
NA
2

Должен выводиться как

Bobby
1.5

Вот моя существующая программа, созданная с помощью отсюда. Любая помощь приветствуется!

with open('C://avy.txt', "rtU") as f:
    columns = f.readline().strip().split(" ")
    numRows = 0
    sums = [0] * len(columns)

    for line in f:
        # Skip empty lines
        if not line.strip():
            continue

        values = line.split(" ")
        for i in xrange(len(values)):
            sums[i] += int(values[i])
        numRows += 1

        with open('c://finished.txt', 'w') as ouf:
             for index, summedRowValue in enumerate(sums):
                 print>>ouf, columns[index], 1.0 * summedRowValue / numRows

Теперь у меня есть это:

с открытым ('C: //avy.txt', "rtU") как f:

def get_averages(f):
   headers = f.readline().split()
   ncols = len(headers)
   sumx0 = [0] * ncols
   sumx1 = [0.0] * ncols
   lino = 1

for line in f:
   lino += 1
   values = line.split()

for colindex, x in enumerate(values):
        if colindex >= ncols:
             print >> sys.stderr, "Extra data %r in row %d, column %d" %(x, lino, colindex+1)
             continue
             try:
                value = float(x)
             except ValueError:
               continue
               sumx0[colindex] += 1
        sumx1[colindex] += value
        print headers
print sumx1
print sumx0
averages = [
    total / count if count else None
   for total, count in zip(sumx1, sumx0)
    ]
print averages

и там написано:

Traceback (последний последний вызов): Файл "C: /avy10.py", строка 11, в лино + = 1 NameError: имя 'lino' не определено

Ответы [ 5 ]

3 голосов
/ 23 сентября 2010

Вот функциональное решение:

text = """Joe Sam Bob
1   2   3
2   1   3
NA 2 3
3 5 NA"""

def avg( lst ):
    """ returns the average of a list """
    return 1. * sum(lst)/len(lst)

# split that text
parts = [line.split() for line in text.splitlines()]
#remove the headers
names = parts.pop(0)
# zip(*m) does something like transpose a matrix :-)
columns = zip(*parts)
# convert to numbers and leave out the NA
numbers = [[int(x) for x in column if x != 'NA' ] for column in columns]
# all left is averaging
averages = [avg(col) for col in numbers]
# and printing
for name, x in zip( names, averages):
    print name, x

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

2 голосов
/ 22 сентября 2010

[отредактировано для ясности]

При чтении элементов из текстового файла они импортируются как строки, а не числа.Это означает, что если ваш текстовый файл имеет номер 3 и вы читаете его в Python, вам необходимо преобразовать строку в число перед выполнением арифметических операций.

Теперь у вас есть текстовый файл сcolums.Каждый столбец имеет заголовок и коллекцию элементов.Каждый элемент является либо номером, либо нет.Если это число, оно будет правильно преобразовано функцией float, если оно не является действительным числом (то есть, если преобразование не существует), преобразование вызовет исключение, называемое ValueError.

Таким образом, вы просматриваете свой список и элементы, как это было правильно объяснено в более чем одном ответе.Если вы можете конвертировать в float, накопите статистику.Если нет, продолжайте игнорировать эту запись.

Если вам нужна дополнительная информация о том, что такое «печать утки» (парадигма, которую можно возобновить как «лучше попросить прощения, чем разрешение»), пожалуйста, проверьте1012 * Ссылка на Википедию .Если вы попадаете на Python, вы очень часто будете слышать этот термин.

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

class Accumulator(object):
    """
    Used to accumulate the arithmetic mean of a stream of
    numbers. This implementation does not allow to remove items
    already accumulated, but it could easily be modified to do
    so. also, other statistics could be accumulated.
    """
    def __init__(self):
     # upon initialization, the numnber of items currently
     # accumulated (_n) and the total sum of the items acumulated
     # (_sum) are set to zero because nothing has been accumulated
     # yet.
     self._n = 0
     self._sum = 0.0

    def add(self, item):
     # the 'add' is used to add an item to this accumulator
     try:
        # try to convert the item to a float. If you are
        # successful, add the float to the current sum and
        # increase the number of accumulated items
        self._sum += float(item)
        self._n += 1
     except ValueError:
        # if you fail to convert the item to a float, simply
        # ignore the exception (pass on it and do nothing)
        pass

    @property
    def mean(self):
     # the property 'mean' returns the current mean accumulated in
     # the object
     if self._n > 0:
        # if you have more than zero items accumulated, then return
        # their artithmetic average
        return self._sum / self._n
     else:
        # if you have no items accumulated, return None (you could
        # also raise an exception)
        return None

# using the object:

# Create an instance of the object "Accumulator"
my_accumulator = Accumulator()
print my_accumulator.mean
# prints None because there are no items accumulated

# add one (a number)
my_accumulator.add(1)
print my_accumulator.mean
# prints 1.0

# add two (a string - it will be converted to a float)
my_accumulator.add('2')
print my_accumulator.mean
# prints 1.5

# add a 'NA' (will be ignored because it cannot be converted to float)
my_accumulator.add('NA')
print my_accumulator.mean
# prints 1.5 (notice that it ignored the 'NA')

Приветствия.

0 голосов
/ 23 сентября 2010

Следующий код правильно обрабатывает различные значения, а также обнаруживает дополнительные данные ... другими словами, он достаточно надежный.Это может быть улучшено явными сообщениями (1), если файл пуст (2), если строка заголовка пуста.Другой возможностью является явное тестирование на "NA" и выдача сообщения об ошибке, если поле не является ни "NA", ни плавающим.

>>> import sys, StringIO
>>>
>>> data = """\
... Jim Joe Billy Bob
... 1   2   3     x
... 2   x   x     x  666
...
... 3   4   5     x
... """
>>>
>>> def get_averages(f):
...     headers = f.readline().split()
...     ncols = len(headers)
...     sumx0 = [0] * ncols
...     sumx1 = [0.0] * ncols
...     lino = 1
...     for line in f:
...         lino += 1
...         values = line.split()
...         for colindex, x in enumerate(values):
...             if colindex >= ncols:
...                 print >> sys.stderr, "Extra data %r in row %d, column %d" %
(x, lino, colindex+1)
...                 continue
...             try:
...                 value = float(x)
...             except ValueError:
...                 continue
...             sumx0[colindex] += 1
...             sumx1[colindex] += value
...     print headers
...     print sumx1
...     print sumx0
...     averages = [
...         total / count if count else None
...         for total, count in zip(sumx1, sumx0)
...         ]
...     print averages

Редактировать добавить сюда:

...     return headers, averages

...
>>> sio = StringIO.StringIO(data)
>>> get_averages(sio)
Extra data '666' in row 3, column 5
['Jim', 'Joe', 'Billy', 'Bob']
[6.0, 6.0, 8.0, 0.0]
[3, 2, 2, 0]
[2.0, 3.0, 4.0, None]
>>>

Редактировать

Обычное использование:

with open('myfile.text') as mf:
   hdrs, avgs = get_averages(mf)
0 голосов
/ 22 сентября 2010

Гораздо меньший код:

with open('in', "rtU") as f:
    lines = [l for l in f if l.strip()]
    names = '\t'.join(lines[0].split())
    numbers = [[i.strip() for i in line.split()] for line in lines[1:]]
    person_data = zip(*numbers)
    person_data = [tuple(int(i) for i in t if i!="NA") for t in person_data]
    averages = map(lambda x: str(float(sum(x))/len(x)), person_data)

with open('out', 'w') as f:
    f.write(names)
    f.write('\n')
    f.write('\t'.join(averages))

Я проверил это после того, как Джон Мачин оставил свой комментарий.В ответ на его комментарии:

  1. Это была ошибка, которая существовала, потому что я неправильно прочитал проблему.Это было исправлено
  2. Я пытался сделать эту строку немного более читабельной, но, если честно, я не понимаю, почему вы назвали ее в первую очередь запутанной
  3. У вас естьуказал логическую ошибку в моем коде.Я думаю, что я действительно не должен был делать это в середине класса ... за это я извиняюсь
  4. Я согласен, что readlines () был излишним.У меня не было надлежащего интерпретатора Python для перекрестной проверки, поэтому я оставил его в качестве безопасности

Надеюсь, это лучше.

0 голосов
/ 22 сентября 2010

Измените свой самый внутренний цикл на:

    values = line.split(" ")
    for i in xrange(len(values)):
        if values[i] == "NA":
            continue
        sums[i] += int(values[i])
    numRows += 1
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...