Понимание списка Python и другие лучшие практики - PullRequest
3 голосов
/ 30 августа 2010

Это относится к проекту по преобразованию двухсторонней программы ANOVA в SAS в Python.

Я в значительной степени начал пытаться изучать язык в четверг, поэтому я знаю, что у меня много возможностей для совершенствования. Если я что-то упустил очевидное, во что бы то ни стало, дайте мне знать. Я еще не запустил Sage и не работал, так что прямо сейчас, это все довольно ванильный Python 2.6.1. (Портативный)

Основной запрос: нужен хороший набор понятий для списков, которые могут извлекать данные в списках выборок в списках по фактору A, по фактору B в целом и по группам каждого уровня факторов A & B (AxB).

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

ответ [A] [B] [N]

(имеется в виду [a1 [b1 [n1, ..., nN] ... [bB [n1, ... nN]]], ..., [aA [b1 [n1, ..., nN ] ... [bB [n1, ... nN]]] Надеюсь, это понятно.)

Уровни фактора в моем примере: A = 3 (0-2), B = 8 (0-7), N = 8 (0-7)

byA= [[a[i] for i in range(b)] for a[b] in response]

(Может кто-нибудь объяснить , почему этот синтаксис работает? Я наткнулся на него, пытаясь понять, что синтаксический анализатор примет. хорошие ссылки на сайты или книги по этой теме приветствуются. Редактирование: Постоянство переменных между прогонами объяснило эту странность. Это не работает.)

byB=lstcrunch([[Bs[i] for i in range(len(Bs)) ]for Bs in response])

(Следует отметить, что zip(*response) почти делает то, что я хочу. Вышеприведенная версия на самом деле не работает, насколько я помню. Я еще не прошел тщательный тест.)

byAxB= [item for sublist in response for item in sublist]

(Украдено из ответа Алекса Мартелли на этом сайте. Опять может кто-нибудь объяснить почему ? Синтаксис понимания списка не очень хорошо объяснен в текстах, которые я читал.)

ByO= [item for sublist in byAxB for item in sublist]

(Очевидно, я просто использовал здесь прежнее понимание, потому что оно сделало то, что мне нужно. Редактировать:)

Я бы хотел, чтобы эти типы данных заканчивались одним и тем же типом, по крайней мере, когда он проходит через рассматриваемый фактор, s.t. те же самые средние / sum / SS / и так далее функции могут применяться и использоваться.

Это можно легко заменить чем-нибудь более чистым:

def lstcrunch(Dlist):
    """Returns a list containing the entire
    contents of whatever is imported,
    reduced by one level.

    If a rectangular array, it reduces a dimension by one.
    lstcrunch(DataSet[a][b]) -> DataOutput[a]
    [[1, 2], [[2, 3], [2, 4]]] -> [1, 2, [2, 3], [2, 4]]
    """
    flat=[]
    if islist(Dlist):#1D top level list
        for i in Dlist:
            if islist(i):
                flat+= i
            else:
                flat.append(i)
        return flat
    else:
        return [Dlist]

О, пока я нахожусь в теме, каков предпочтительный способ идентификации переменной в виде списка? Я использовал:

def islist(a):
    "Returns 'True' if input is a list and 'False' otherwise"
    return type(a)==type([])

Запрос на расставание: Есть ли способ явно заставить мелкую копию преобразовать в глубокую? копировать? Или, аналогично, при копировании в переменную, есть ли способ объявить, что присвоение также должно заменить указатель, а не просто значение? (s.t. назначение не будет распространяться на другие мелкие копии) Точно так же использование этого также может быть полезно время от времени, поэтому возможность контролировать, когда это происходит или не происходит, звучит очень хорошо. (Я действительно перешагнул через себя, когда я подготовил свой стол для вставки, позвонив: ответ = [[[0] * N] * B] * А )

Редактировать : Дальнейшее расследование привело к тому, что большая часть этого работала нормально. С тех пор я сделал класс и проверил его. это работает отлично. Я оставлю формы понимания списка нетронутыми для справки.

def byB(array_a_b_c):
    y=range(len(array_a_b_c))
    x=range(len(array_a_b_c[0]))
    return [[array_a_b_c[i][j][k]
    for k in range(len(array_a_b_c[0][0]))
    for i in y]
    for j in x]


def byA(array_a_b_c):
    return [[repn for rowB in rowA for repn in rowB] 
    for rowA in array_a_b_c]

def byAxB(array_a_b_c):
    return [rowB for rowA in array_a_b_c 
    for rowB in rowA]

def byO(array_a_b_c):
    return [rep
    for rowA in array_a_b_c
    for rowB in rowA
    for rep in rowB]


def gen3d(row, col, inner):
"""Produces a 3d nested array without any naughty shallow copies.

[row[col[inner]] named s.t. the outer can be split on, per lprn for easy display"""
    return [[[k for k in range(inner)]
    for i in range(col)]
    for j in range(row)]

def lprn(X):
    """This prints a list by lines.

    Not fancy, but works"""
    if isiterable(X):
        for line in X: print line
    else:
        print x

def isiterable(a):
    return hasattr(a, "__iter__")

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

Ответы [ 3 ]

6 голосов
/ 30 августа 2010

byAxB= [item for sublist in response for item in sublist] Опять может кто-нибудь объяснить, почему?

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

Я бы подошел к этому слева направо.Возьмите эти четыре слова:

for sublist in response

Надеюсь, вы сможете увидеть сходство с обычным циклом for.Эти четыре слова делают основную работу по выполнению некоторых действий над каждым sublist в response.Похоже, что response это список списков.В этом случае sublist будет списком для каждой итерации до response.

for item in sublist

Это снова очередной цикл for в процессе создания.Учитывая, что мы впервые услышали о sublist в предыдущем «цикле», это указывало бы на то, что мы теперь пересекаем подсписок, по одному item за раз.Если бы я выписал эти циклы без понимания, это выглядело бы так:

for sublist in response:
    for item in sublist:

Далее мы рассмотрим остальные слова.[, item и ].Это фактически означает, собирать элементы в списке и возвращать полученный список.

Всякий раз, когда у вас возникают проблемы при создании или понимании итераций списка, напишите соответствующие циклы for и затем сожмите их:

result = []

for sublist in response:
    for item in sublist:
        result.append(item)

Это сжимает до:

[
    item 
    for sublist in response
    for item in sublist
]

Синтаксис понимания списка не очень хорошо объяснен в текстах, которые я читал

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

Обновление

Я забыл что-то сказать.Понимание списка - это еще один способ достижения того, что традиционно делалось с использованием map и filter.Было бы неплохо понять, как работают map и filter, если вы хотите улучшить свое понимание.

1 голос
/ 30 августа 2010

Иногда странно производить правильный уровень рекурсии в вашей структуре данных, однако я думаю, что в вашем случае это должно быть относительно просто.Чтобы проверить это во время работы, нам понадобится один пример данных, скажем:

data = [ [a,
          [b,
           range(1,9)]]
         for b in range(8)
         for a in range(3)]
print 'Origin'
print(data)
print 'Flat'
## from this we see how to produce the c data flat
print([(a,b,c) for a,[b,c] in data])    
print "Sum of data in third level = %f" % sum(point for point in c for a,[b,c] in data)
print "Sum of all data = %f" % sum(a+b+sum(c) for a,[b,c] in data)

для проверки типа, как правило, этого следует избегать, но если это необходимо, например, когда вы не хотите делать рекурсию вСтрока, вы можете сделать это следующим образом

if not isinstance(data, basestring) : ....

Если вам нужно сгладить структуру, вы можете найти полезный код в Документация Python (другой способ выразить это chain(*listOfLists)) и как понимание списка[ d for sublist in listOfLists for d in sublist ]:

from itertools import flat.chain
def flatten(listOfLists):
    "Flatten one level of nesting"
    return chain.from_iterable(listOfLists)

Это не работает, если у вас есть данные на разных глубинах. Для выравнивателя большой массы см .: http://www.python.org/workshops/1994-11/flatten.py,

1 голос
/ 30 августа 2010

Что касается части копирования, загляните в модуль копирования. Python просто использует ссылки после создания первого объекта, поэтому любые изменения в других «копиях» распространяются обратно на оригинал, но модуль копирования делает реальные копии объектов, и выможно указать несколько режимов копирования

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