【问题标题】:Slicy every nth element, not beginnint with the first切片每个第 n 个元素,而不是从第一个元素开始
【发布时间】:2020-12-03 14:55:14
【问题描述】:

如何优雅地将 python 列表分成两个,以便第二个具有第一个的每个第 n 个元素,并将这些切片元素从第一个列表中删除? 切片不应从第一个元素开始!

例子:

split_data([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])

应该返回 ([1,2,3,4,6,7,8,9,11,12,13,14], [5,10,15])

谢谢你:)

编辑: 对于选择每第 n 个元素的部分,我尝试了以下方法:

test = data[::5]
train = data
del data[::5]
return (train, test)

然而,这只会为split_data(list(range(1, 30))) 返回([2, 3, 4, 5, 7, 8, 9, 10, 12, ...], [1, 6, 11, 16, 21, 26])优雅我想表达我想避免使用for循环来遍历列表;)

【问题讨论】:

  • 你已经尝试过什么“不优雅”?
  • 你能告诉我们你到目前为止尝试了什么以及为什么它不能解决你的问题吗?
  • 纯代码编写请求在 Stack Overflow 上是题外话——我们希望这里的问题与特定编程问题有关——但我们很乐意帮助您自己编写!告诉我们what you've tried,以及您遇到的问题。这也将有助于我们更好地回答您的问题。
  • 感谢您的建议,我已经编辑了我的帖子。
  • 顺便说一句:从您的train/test 名称看来,您正在做分​​类/机器学习的工作。那么你可能/应该使用 numpy 数组或 pandas 数据框或类似的东西而不是列表。这样可以做得更优雅

标签: python list slice


【解决方案1】:

您可以利用list.pop() 按索引删除元素并返回它。因此,您的原始列表将不包含这些数字,通过使用弹出的项目创建一个新列表,您可以获得第二个列表。

def split(l,n):
    return (l, [l.pop(i) for i in range(n, len(l), n)])

>>>l = list(range(1,16))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]

>>>split(l,4)
([1, 2, 3, 4, 6, 7, 8, 9, 11, 12, 13, 14], [5, 10, 15])

这将改变作为参数传递的列表。如果您希望您的函数保持原样,只需在与l 混淆之前添加l=list(l)

感谢@abstractbyte,此答案无效,因为list.pop() 在迭代时会减少列表的长度,最终导致索引超出范围。

但是,使用numpypandas 仍然可以“优雅地”解决同样的问题:

  • numpy:
def slice_numpy(l, n):
    l = np.array(l) 
    mask = list(range(n-1,len(l),n))
    return np.delete(l, mask), l[mask]
    # note that `len` and `delete` will work as expected on 1D arrays. if you have a 2D dataset you need to modify them accordingly
  • 熊猫
def slice_pandas(l, n):
    l = pd.Series(l)
    mask = list(range(n-1,len(l),n))
    return l.drop(mask), l[mask]

  • 示例:
>>> l = list(range(1, 16))
>>> l
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>>>
>>>
>>> train, test = slice_numpy(l, 5)
>>> train
array([ 1,  2,  3,  4,  6,  7,  8,  9, 11, 12, 13, 14])
>>> test
array([ 5, 10, 15])
>>>
>>>
>>> train, test = slice_pandas(l, 5)
>>> train
0      1
1      2
2      3
3      4
5      6
6      7
7      8
8      9
10    11
11    12
12    13
13    14
dtype: int64
>>> test
4      5
9     10
14    15
dtype: int64


【讨论】:

  • 非常感谢,我不知道pop()的这个功能:)
  • 使用pop() 在您提供的示例中确实有效,但是对于长度(甚至稍微)更大的列表,代码会引发执行,因为当我们弹出列表的元素时,我们会减少它的大小,迟早,我们会尝试访问不再在列表中的索引。
  • 这是真的。我正在更新我的答案
  • 现在看看
  • 这是一个非常有趣的方法。它工作正常;)再次感谢您