В Python самой базовой формой для нарезки является следующее:
l[start:end]
, где l
- некоторая коллекция, start
является инклюзивным индексом и end
является эксклюзивным индексом.
In [1]: l = list(range(10))
In [2]: l[:5] # first five elements
Out[2]: [0, 1, 2, 3, 4]
In [3]: l[-5:] # last five elements
Out[3]: [5, 6, 7, 8, 9]
При нарезке с начала вы можете опустить нулевой индекс, а при разрезании до конца вы можете опустить конечный индекс, поскольку он является избыточным, поэтому не нужно verbose:
In [5]: l[:3] == l[0:3]
Out[5]: True
In [6]: l[7:] == l[7:len(l)]
Out[6]: True
Отрицательные целые числа полезны при выполнении смещений относительно конца коллекции:
In [7]: l[:-1] # include all elements but the last one
Out[7]: [0, 1, 2, 3, 4, 5, 6, 7, 8]
In [8]: l[-3:] # take the last 3 elements
Out[8]: [7, 8, 9]
Можно предоставить индексы, которые выходят за рамки, когда таких как:
In [9]: l[:20] # 20 is out of index bounds, l[20] will raise an IndexError exception
Out[9]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [11]: l[-20:] # -20 is out of index bounds, l[-20] will raise an IndexError exception
Out[11]: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Имейте в виду, что результат нарезки коллекции - это совершенно новая коллекция. Кроме того, при использовании нотации среза в назначениях длина назначения среза не обязательно должна быть одинаковой. Значения до и после назначенного среза будут сохранены, и коллекция будет сжиматься или увеличиваться, чтобы содержать новые значения:
In [16]: l[2:6] = list('abc') # assigning less elements than the ones contained in the sliced collection l[2:6]
In [17]: l
Out[17]: [0, 1, 'a', 'b', 'c', 6, 7, 8, 9]
In [18]: l[2:5] = list('hello') # assigning more elements than the ones contained in the sliced collection l [2:5]
In [19]: l
Out[19]: [0, 1, 'h', 'e', 'l', 'l', 'o', 6, 7, 8, 9]
Если вы опустите индекс начала и конца, вы сделаете копию сбор:
In [14]: l_copy = l[:]
In [15]: l == l_copy and l is not l_copy
Out[15]: True
Если начальные и конечные индексы опущены при выполнении операции присваивания, все содержимое коллекции будет заменено копией того, на что ссылаются:
In [20]: l[:] = list('hello...')
In [21]: l
Out[21]: ['h', 'e', 'l', 'l', 'o', '.', '.', '.']
Помимо базового разреза, также можно применить следующие обозначения:
l[start:end:step]
, где l
является коллекцией, start
является инклюзивным индексом, end
является эксклюзивным index и step
- это шаг, который может использоваться для приема каждого пункта nth в l
.
In [22]: l = list(range(10))
In [23]: l[::2] # take the elements which indexes are even
Out[23]: [0, 2, 4, 6, 8]
In [24]: l[1::2] # take the elements which indexes are odd
Out[24]: [1, 3, 5, 7, 9]
Использование step
обеспечивает полезный трюк для обратного коллекция в Python:
In [25]: l[::-1]
Out[25]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
Также возможно использовать отрицательные целые числа для step
в следующем примере:
In[28]: l[::-2]
Out[28]: [9, 7, 5, 3, 1]
Однако, используя отрицательное значение для step
может стать очень запутанным. Более того, чтобы быть Pythonic, вам следует избегать использования start
, end
и step
в одном фрагменте. В случае, если это необходимо, подумайте об этом в двух назначениях (один для среза, а другой - для шага).
In [29]: l = l[::2] # this step is for striding
In [30]: l
Out[30]: [0, 2, 4, 6, 8]
In [31]: l = l[1:-1] # this step is for slicing
In [32]: l
Out[32]: [2, 4, 6]
Как насчет следующего решения. Я использую более экстремальный пример сложного взаимодействия.
f = formula(y ~ a * b * c * d * e)
Чтобы изложить условия взаимодействия, мы извлекаем термины из значения, возвращаемого terms.formula ():
terms = attr(terms.formula(f), "term.labels")
, который дает:
> terms
[1] "a" "b" "c" "d" "e" "a:b" "a:c"
[8] "b:c" "a:d" "b:d" "c:d" "a:e" "b:e" "c:e"
[15] "d:e" "a:b:c" "a:b:d" "a:c:d" "b:c:d" "a:b:e" "a:c:e"
[22] "b:c:e" "a:d:e" "b:d:e" "c:d:e" "a:b:c:d" "a:b:c:e" "a:b:d:e"
[29] "a:c:d:e" "b:c:d:e" "a:b:c:d:e"
И тогда мы можем преобразовать его обратно в формулу:
f = as.formula(sprintf("y ~ %s", paste(terms, collapse="+")))
> f
y ~ a + b + c + d + e + a:b + a:c + b:c + a:d + b:d + c:d + a:e +
b:e + c:e + d:e + a:b:c + a:b:d + a:c:d + b:c:d + a:b:e +
a:c:e + b:c:e + a:d:e + b:d:e + c:d:e + a:b:c:d + a:b:c:e +
a:b:d:e + a:c:d:e + b:c:d:e + a:b:c:d:e
Посмотрите на справку для formula
могут существовать вещи, которые будут работать для вас.
Например, формула y ~ (a + b + c + d)^2
даст вам все основные эффекты и все двухсторонние взаимодействия и формулу y ~ (a + b) * (c + d)
дает расширение, которое вы показываете выше. Вы также можете вычитать термины, поэтому y ~ a*b*c - a:b:c
не будет включать трехстороннее взаимодействие.
У нас была аналогичная проблема, но немного проще - в формуле мы получили 50 переменных, и нам приходилось менять их очень часто; наше решение заключалось в том, чтобы внутри R-скрипта отправлять их в цикле во внешний файл, делая фактическую формулу, а затем просто читать этот txt-файл и вставлять его; насколько я помню, это можно было бы сделать в вложенном цикле, чтобы сделать больше формул, а затем прочитать файл строки за строкой; в целом, всегда полезно использовать как R-скрипты, так и bash
Мне еще предстоит изучить все трюки формулы, но если мне нужны явные формулы, я буду использовать sapply вместе со вставкой:
# the factors
fac1 <- factor(c('a', 'a', 'b', 'b'))
fac2 <- factor(c('c', 'd', 'c', 'd'))
# create all the interaction terms
out <- sapply(levels(fac1), function(ii) {
sapply(levels(fac2), function(jj) {
paste0(ii,":",jj)
})
})
# along with the single terms
terms <- c(levels(fac1), levels(fac2), as.vector(out))
# and create the rhs of the formula
rhs <- paste0(terms, collapse=" + ")
# finally add the lhs
f <- paste0("x ~ ", rhs)
В итоге получим:
> f
[1] "x ~ a + b + c + d + a:c + a:d + b:c + b:d"