如何对列表列表进行排序,并按间隔仅保留每个第一元素的最大第二元素? 初始版本尚无答案最终版本进一步的优化基准化

如何解决如何对列表列表进行排序,并按间隔仅保留每个第一元素的最大第二元素? 初始版本尚无答案最终版本进一步的优化基准化

这是this question的较硬版本,但我无法有效解决(最好,而无需导入库)。

假设我有一些清单:

lst = [[1,2],[1,4],6],[2,3],[3,5],[7,8]]

假设我有一个间隔列表:

intervals = [0,3,5,8]

我想在每个间隔中保留一个第一个元素的子列表和一个具有最高第二个元素的子列表。在此示例中,这意味着将只有一个第一个元素在0和3之间的子列表,一个只有第一个元素在3和5之间的子列表,依此类推,因此结果将是:

result:
>>> [[1,8]]

请注意:

  • 以{0 =
  • 最好,例如,如果我们有[1,6]和[2,6]的间隔与 将保留的是第一个元素最低的元素([1,6])

解决方法

以下是三种解决方案,按性能排序:

  1. 为每个元素中的第一个/第二个数字创建两个列表。它增加了内存使用量,但却是最快的选择。

  2. 使用key中的max参数获得第二个数字最高的元素。避免重复使用内存,但速度要慢30%。这可能是一个很好的中间立场。

  3. 使用itertools.groupbykey function来获取每个元素中第一个数字的间隔。它可以用于更健壮的应用程序,但是效率不高,它迭代Intervals直到找到匹配间隔为止的每个元素。它比第一种选择慢了近三倍。


选项1:创建两个列表

将列表分为两个列表,以列出每个元素的第一个/第二个数字。

# sort and separate lst
lst = sorted(lst)
first = [e[0] for e in lst]
second = [e[1] for e in lst]

# iterate upper limits of intervals and get max of each sublist
i = k = 0
keep = []
while lst[i][0] < Intervals[0]:
    i += 1
for upper in Intervals[1:]:
    k = sum(f < upper for f in first[i:])
    keep.append(i + second[i:i+k].index(max(second[i:i+k])))
    i += k

result = [lst[i] for i in keep]
print(result)

输出

[[1,6],[3,5],[7,8]]

选项2:使用max(lst,key)

您可以使用max(lst,key=lambda x: x[1])获得第二个数字最大的元素。这是间隔的实现。

lst = sorted(lst)

i = k = 0
result = []
for upper in Intervals:
    i += k
    # old solution summed a generator
    # k = sum(e[0] < upper for e in lst[i:])
    # this one uses a while-loop to avoid checking the rest of the list on each iteration
    # a good idea if `lst` is long and `Intervals` are many
    k = 0
    while i + k < len(lst) and lst[i+k][0] < upper: 
        k += 1
    if upper == Intervals[0]:
        continue
    result.append(max(lst[i:i+k],key=lambda x:x[1]))

输出

[[1,8]]

选项3:itertools.groubpy(lst,key)

from itertools import groupby

def get_bin(element,bins):
    x = element[0]
    if x < bins[0]:
        return -1
    elif x in bins:
        return bins.index(x)
    else:
        for i,b in enumerate(bins[1:]):
            if x < b:
                break
        return i
        

result = sorted([
    max(items,key=lambda x: x[1])
    for _,items in groupby(lst,lambda x: get_bin(x,Intervals))
])

输出

[[1,8]]
,

为简单起见:

lst = [[1,2],[1,4],[2,3],8]]
intervals = [0,3,5,8] #usually,variables starts lowercase

初始版本(尚无答案)

我将演示如何根据intervals 中的索引将列表分为几组,然后在此处返回每个组的最大项。您可以使用一个技巧,我想将其称为数组的“ shift”:

def get_groups(lst,intervals):
    return [lst[i:j] for i,j in zip(intervals[:-1],intervals[1:])]

这是一种构造切片元组的好方法,这些切片是:(0,3)(3,5)(5,8)。现在您有了:

>>> groups = get_groups(lst,interval)
>>> groups
[[[1,6]],[[2,3]],[[3,8]]]

然后在按第二列排序时提取最大元素:

>>> [max(n,key = lambda x: x[1]) for n in groups]
[[1,8]]

重要的是要区分第二列具有相同值的两个项目:

[max(n,key = lambda x: (x[1],x[0])) for n in groups]

最终版本

相反,

OP需要根据属于intervals 的值将列表分为几组。如果列表已预先排序,则有可能在第一个结果的顶部构建算法,并且我们正在对数组进行一次搜索,以查找应该插入元素以保持顺序的索引。在这种情况下,get_groups应该重新定义如下:

def get_groups(lst,intervals):
    lst = sorted(lst)
    firstcolumn = [n[0] for n in lst]
    intervals = searchsorted(first_column,intervals)
    return [lst[i:j] for i,intervals[1:])]

目前,您还可以使用RichieV答案的改编版本:

def searchsorted(array,intervals):
    idx,i,n = [],len(array)
    for upper in intervals:
        while array[i] < upper:
            i += 1
            if i == n:
                idx.append(n)
                return idx
        else:
            idx.append(i)
    return idx

>>> searchsorted([1,1,2,7],[0,8])
[0,6,7]

请注意,get_groups并不是最佳选择,因为first_columnlst都被迭代了两次。

用法:

def simple_sol(lst,intervals):
    return [max(n,key=lambda x: x[1]) for n in get_groups(lst,intervals)]
#Output: [[1,8]]

进一步的优化

我写了一个由替代方法np.searchsorted启发的searchsorted的定义,该方法基于二进制搜索。效率也更高(O(m log(n))O(mn))。对于Python版本,另请参阅bisect.bisect_leftdocs中的source coderelated answer关于二进制搜索。这是双赢的C级+二进制搜索(与我的previous answer差不多):

def binsorted(lst,intervals):
    lst = np.array(lst)
    lst = lst[np.argsort(lst[:,0])] #sorting lst by first row
    idx = np.searchsorted(lst[:,0],intervals)
    if idx[-1] == len(lst):
        return np.maximum.reduceat(lst,idx[:-1],axis=0)
    else:
        return np.maximum.reduceat(lst,idx,axis=0)

#Output: [[2,8]]

基准化

我比较了样本的option1option2option3simple_solbinsorting

lst = np.random.randint(1000,size = (1000000,2)).tolist()
intervals = np.unique(np.random.randint(1000,size = 100)).tolist() + [1000]

timeit是:

18.4 s ± 472 ms per loop (mean ± std. dev. of 7 runs,1 loop each)
4.21 s ± 386 ms per loop (mean ± std. dev. of 7 runs,1 loop each)
10.3 s ± 410 ms per loop (mean ± std. dev. of 7 runs,1 loop each)
4.12 s ± 202 ms per loop (mean ± std. dev. of 7 runs,1 loop each)
1.38 s ± 97.2 ms per loop (mean ± std. dev. of 7 runs,1 loop each)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-