Après ces fêtes de fin d'années, aplatissons… des listes
#DynamoBIM #Python #List #Flatten
#AutodeskExpertElite #AutodeskCommunity
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
Il existe plusieurs méthodes en Python pour aplatir une liste, voyons en
revus quelques méthodes disponibles dans l'environnement DynamoBim
-
l'utilisation de l'API Dynamo avec la méthode
DSCore.List.Flatten()
- l'utilisation d'un générateur python (fonction récursive)
-
l'utilisation de la méthode d'extension Linq
SelectMany
(fonction récursive) -
l'utilisation du package Matplotlib avec la méthode
matplotlib.cbook.flatten
Pour les tests, nous utiliserons ici le nouveau moteur PythonNet3, avec
comme entrée de tests une liste Python et une Collection .Net qui contiennent plus de 30000
objets repartis dans différentes listes imbriquées.
Nous utilisons ici une liste non homogène
code Python pour les Tests
import sys
import clr
import System
#import net library
from System import Array, GC
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference('DSCoreNodes')
from DSCore import List as DSCoreList
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
net_clr_runtime = "Net Runtime : " + str(System.Environment.Version)
import traceback
import datetime
import matplotlib
def decotimeit(n_try):
def timeit(func):
def timeit_wrapper(*args, **kwargs):
a = datetime.datetime.now()
for i in range(n_try):
result = func(*args, **kwargs)
b = datetime.datetime.now() - a
mean_time = (b.microseconds / n_try) * 0.001
print(f"""Function {func.__name__} with input type : {type(args[0])}
| result length : {len(result)}
| average time {mean_time:.3f} milliseconds ({n_try} tests)
| {net_clr_runtime}""")
return result
return timeit_wrapper
return timeit
@decotimeit(n_try = 10)
def Test_flatten_with_DSCoreList(input_lst):
if sys.implementation.name == "cpython" and clr.__version__.startswith("3.") and isinstance(input_lst, list):
# currently with DSPythonNet3 need to convert input_lst to NetList
return DSCoreList.Flatten(convert_input_lst_to_NetList(input_lst))
else:
return DSCoreList.Flatten(input_lst)
@decotimeit(n_try = 10)
def Test_flatten_with_Matplotlib(input_lst):
return list(matplotlib.cbook.flatten(input_lst))
@decotimeit(n_try = 10)
def Test_flatten_with_RecurseGenerator(input_lst):
# sub function
def flattenWithRecurseGen(*args):
for x in args:
if hasattr(x, "__iter__") and not isinstance(x, (str, System.String)):
for y in flattenWithRecurseGen(*x) : yield y
else: yield x
# main
return list(flattenWithRecurseGen(input_lst))
@decotimeit(n_try = 10)
def Test_flatten_with_RecurseLinq(input_lst):
# sub function
def flattenWithRecurseLinq(iList):
if isinstance(iList, list):
iList = List[System.Object](iList)
iList = iList.SelectMany[System.Object, System.Object](
System.Func[System.Object, List[System.Object]](
lambda p : flattenWithRecurseLinq(p) if hasattr(p, "__iter__")\
and not isinstance(p, (str, System.String)) \
else List[System.Object]([p])
)
).Select(System.Func[System.Object, System.Object](lambda p : p))\
.ToList()
return iList
# main
return flattenWithRecurseLinq(input_lst)
def convert_input_lst_to_NetList(lst):
"""Convert python list and nested list to Net List"""
newlst = List[System.Object](lst) # convert to Net List
for idx, elem in enumerate(lst):
if isinstance(elem, list) and len(elem) > 0:
newlst[idx] = convert_input_lst_to_NetList(lst[idx])
elif isinstance(elem, list) and len(elem) == 0:
newlst[idx] = List[System.Object]([])
else:
newlst[idx] = lst[idx]
return newlst
python_input_lst = IN[0]
net_input_lst = convert_input_lst_to_NetList(IN[0])
# clear Gabarge collector for tests
GC.Collect()
print("-" * 10)
result_A1 = Test_flatten_with_DSCoreList(python_input_lst)
result_A2 = Test_flatten_with_DSCoreList(net_input_lst)
print("-" * 10)
result_B1 = Test_flatten_with_RecurseGenerator(python_input_lst)
result_B2 = Test_flatten_with_RecurseGenerator(net_input_lst)
print("-" * 10)
result_C1 = Test_flatten_with_RecurseLinq(python_input_lst)
result_C2 = Test_flatten_with_RecurseLinq(net_input_lst)
print("-" * 10)
result_D1 = Test_flatten_with_Matplotlib(python_input_lst)
result_D2 = Test_flatten_with_Matplotlib(net_input_lst)
print("-" * 10)
OUT = result_C2
résultats
Function | Input Type | Average Result Time for 10 tests (ms) | Compatibility Engine |
---|---|---|---|
Test_flatten_with_DSCoreList | list (python) | 48.386 | CPython3, PythonNet3, IronPython3 |
Test_flatten_with_DSCoreList | System.Collections.Generic.List | 0.400 | CPython3, PythonNet3, IronPython3 |
Test_flatten_with_RecurseGenerator | list (python) | 62.672 | CPython3, PythonNet3, IronPython3 |
Test_flatten_with_RecurseGenerator | System.Collections.Generic.List | 93.577 | CPython3, PythonNet3, IronPython3 |
Test_flatten_with_RecurseLinq | list (python) | 14.761 | IronPython3, PythonNet3 |
Test_flatten_with_RecurseLinq | System.Collections.Generic.List | 8.638 | IronPython3, PythonNet3 |
Test_flatten_with_Matplotlib | list (python) | 10.495 | CPython3, PythonNet3 |
Test_flatten_with_Matplotlib | System.Collections.Generic.List | 38.399 | CPython3, PythonNet3 |
Il en résulte que suivant le type de liste que vous avez en entrée (python ou .Net Collection), une même méthode sera plus ou moins efficace.
Je vous souhaite une Bonne nouvelle Année et plein de bonnes choses
PS : cet article n'a pas été entièrement rédigé par une IA 😝
0 commentaires:
Enregistrer un commentaire