#DataTable #RevitAPI #DynamoBIM #Datas
if this article is not in your language, use the Google Translate
widget ⬈ (bottom of page for Mobile version ⬇)
L'objet
DataTable fait partie de l'API Microsoft et est utilisé pour représenter et
manipuler des données tabulaires dans une structure en mémoire. Il
s'agit d'une classe puissante et polyvalente qui offre des
fonctionnalités pour la création, la modification et la manipulation de
données tabulaires.
Une
DataTable est composée de colonnes et de lignes. Chaque colonne est définie par
son nom et son type de données, tel que texte, nombre, date, etc. Les
lignes représentent les enregistrements individuels contenant les
données dans chaque colonne.
Voici quelques caractéristiques importantes de l'objet
DataTable :
- Structure de colonnes : Vous pouvez définir les colonnes d'une DataTable avec des noms spécifiques et des types de données appropriés. Cela garantit que les données sont organisées de manière cohérente et permet des opérations plus efficaces sur les données.
- Ajout et suppression de données : Vous pouvez ajouter ou supprimer des lignes dans une DataTable pour mettre à jour les données. Cela peut être fait individuellement pour chaque ligne ou en bloc pour plusieurs lignes à la fois.
- Requêtes et filtrage : L'objet DataTable prend en charge des opérations de filtrage et de requêtes pour extraire des sous-ensembles de données spécifiques. Vous pouvez utiliser des expressions LINQ (Language Integrated Query) ou des méthodes intégrées pour effectuer des opérations de recherche, de tri et de filtrage sur les données.
- Relation entre les tables : Vous pouvez également définir des relations entre différentes DataTables en utilisant des clés primaires et des clés étrangères. Cela permet d'établir des liens entre les données de différentes tables et facilite les opérations de jointure et de fusion.
-
Sérialisation et dé sérialisation : Les DataTables peuvent être
facilement sérialisées dans un format tel que XML ou JSON, ce qui
permet de les transmettre via des services Web ou de les enregistrer
dans des fichiers. De plus, elles peuvent être désérialisées pour
récupérer les données et les reconstituer en tant qu'objet
DataTable.
- Plusieurs DataTable peuvent etre contenus dans un objet DataSet
Exemple N°1 : construction d'une DataTable
-
création d'une DataTable avec le constructeur
DataTable()
-
ajout des colonnes en spécifiant le Nom et le Type
(la spécification du Type est optionnel) avec la méthode
DataTable.Columns.Add()
-
Puis, on ajoute les lignes avec DataTable.Rows.Add() en passant en argument autant de valeurs que de colonnes
- Exemple de rajout d'une ligne en haut de la Table (index 0)
import clr
import sys
import System
clr.AddReference("System.Numerics")
#
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
import Autodesk.DesignScript.Geometry as DS
#import Revit API
clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
import Autodesk.Revit.DB as DB
#import net library
from System import Array
from System.Collections.Generic import List, IList, Dictionary
clr.AddReference('System.Data')
from System.Data import *
clr.AddReference('System.Data.DataSetExtensions')
clr.ImportExtensions(System.Data.DataTableExtensions)
#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
all_spaces = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_MEPSpaces).WhereElementIsNotElementType().ToElements()
dt = DataTable("Spaces_DataTable")
dt.Columns.Add('Name', System.String)
dt.Columns.Add('Number', System.String)
dt.Columns.Add('DB_Element', DB.Element)
for e in all_spaces[:-1]:
if e.Number.isdigit():
dt.Rows.Add(e.Name, e.Number , e)
#
# Example to add a Row at Index
# we get the last space and insert at the top (index 0)
row = dt.NewRow();
row["Name"] = all_spaces[-1].Name
row["Number"] = all_spaces[-1].Number
row["DB_Element"] = all_spaces[-1]
dt.Rows.InsertAt(row, 0)
Si l'on passe l'objet DataTable dans un DataGridView (Winform) ou
DataGrid (WPF) voici ce que l'on obtient
import numpy as np
import pandas as pd
all_spaces = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_MEPSpaces).WhereElementIsNotElementType().ToElements()
data ={}
data["Name"] = [x.get_Name() for x in all_spaces]
data["Number"] = [x.Number for x in all_spaces]
data["DB_Elements"] = all_spaces
df = pd.DataFrame.from_dict(data)
Exemple N°2 : suppression de Lignes, ajout de colonnes et ajout d'un index
- Suppression des lignes en double
-
Ajout des colonnes
Area
,Volume
,LevelName
par extraction des propriétés des Espaces - Ajout d'une colonne calculée en se basant sur 2 autres colonnes
-
Définir une colonne comme clé (index)
À titre de comparaison pour chaque type d'opération est rajouté un
exemple avec un DataFrame pandas
# remove dulpicate Rows
dt = dt.DefaultView.ToTable(True)
# example with Pandas
# df = df.drop_duplicates()
#
# Add columns with DB_Element property
dt.Columns.Add('Area', System.Double)
dt.Columns.Add('Volume', System.Double)
dt.Columns.Add('LevelName', System.String)
for row in dt.Rows:
row['Area'] = round(row['DB_Element'].Area, 2) * 0.092903 # convert to m2
row['Volume'] = round(row['DB_Element'].Volume, 2) * 0.0283168 # convert to m3
row['LevelName'] = row['DB_Element'].Level.Name
#
# add a calculated Column
dt.Columns.Add('Height', System.String, "Volume / Area")
# example with Pandas
# df['Area'] = df.apply(lambda x: x['DB_Element'].Area * 0.092903, axis=1)
# df['Volume'] = df.apply(lambda x: x['DB_Element'].Volume * 0.0283168, axis=1)
# df['LevelName'] = df.apply(lambda x: x['DB_Element'].Level.Name, axis=1)
# df['Height'] = df['Volume'] / df['Area']
# set Number Column as Key
keys = System.Array[DataColumn]([DataColumn()])
keys[0] = dt.Columns["Number"]
dt.PrimaryKey = keys
# # similar example with pandas
# df.set_index("Number")
Exemple N°3 : trier les lignes et redéfinir les colonnes
# Sort rows
dataView = dt.DefaultView
dataView.Sort = "LevelName, Number"
# with pandas DataFrame
# df = df.sort_values(['LevelNameY', 'Number'])
# Set New Order of Columns
dt = dataView.ToTable(True, "Number", "Name", "Area", "LevelName")
# with pandas DataFrame
# df.loc[:,["Number", "Name", "Area", "LevelName"]]
Exemple N°4 : rechercher des valeurs dans cette Table de Données
- trouver l'index de la colonne qui sert de clé
# check the column index of primary key
indexKeyColumn = dt.PrimaryKey[0].Ordinal
- trouver une valeur suivant l'index de ligne et l'index de colonne
# find value by indexes
value_2_5 = dt.Rows[2][5]
- trouver une valeur suivant le nom de la clé (ligne) et le nom de la colonne
# Find Row by PrimaryKey
dtRowB = dt.Rows.Find("115")
# find the number of row
indexRowB = dt.Rows.IndexOf(dtRowB)
# Find value PrimaryKey Row and column name
volume_115 = dt.Rows.Find("115")["Area"]
Exemple N°5 : Itérer sur une DataTable
for idxRow, row in enumerate(dt.Rows):
for idxCol, col in enumerate(dt.Columns):
xls_value = row[col] # or row[col.ColumnName]
Exemple N°6 : supprimer des colonnes
# remove a columns
dt.Columns.Remove("columnName")
# or
dt.Columns.Remove(datacolumn)
# or
dt.Columns.RemoveAt(columnIndex)
# remove several columns by set columns to keep
# initial Columns "columnNameA", "columnNameB", "columnNameC", "columnNameD", "columnNameE",
dataView = dt.DefaultView
dt = dataView.ToTable(True, "columnNameA", "columnNameB", "columnNameC")
Exemple N°7 : Ajout de Méthodes sur une DataTable
-
convertir un fichier csv en DataTable →
FromCSVFile()
-
afficher le contenu d'une DataTable →
Show()
-
grouper les lignes suivant les valeurs d'une colonne
et sommer les valeurs des autres colonnes →GroupSumByField()
-
Obtenir les valeurs d'une colonne →
ColumnValues()
import clr
import sys
import re
pyEngineName = sys.implementation.name
import System
clr.AddReference("System.Numerics")
# Import .NET libraries
from System import Array
from System.Collections.Generic import List, IList, Dictionary
# Add references to required .NET assemblies
clr.AddReference('System.Drawing')
clr.AddReference('System.Windows.Forms')
import System.Drawing
import System.Windows.Forms
from System.Drawing import *
from System.Windows.Forms import *
clr.AddReference('System.Data')
from System.Data import *
clr.AddReference('System.Data.DataSetExtensions')
clr.ImportExtensions(System.Data.DataTableExtensions)
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
import csv
class FormDataTable(Form):
"""A Windows Form for displaying a DataTable"""
def __init__(self, datatable, title=""):
"""
Initialize the FormDataTable instance.
Args:
datatable (DataTable): The DataTable to display.
title (str, optional): The title of the form. Defaults to an empty string.
"""
self.datatable = datatable
self.title = title
self.InitializeComponent()
def InitializeComponent(self):
"""Initialize the components of the FormDataTable"""
self._dataGridView1 = System.Windows.Forms.DataGridView()
self._dataGridView1.BeginInit()
# Initialize DataGridView
self.SuspendLayout()
self._dataGridView1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right
self._dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
self._dataGridView1.Location = System.Drawing.Point(12, 33)
self._dataGridView1.Name = "dataGridView1"
self._dataGridView1.DataSource = self.datatable
self._dataGridView1.Size = System.Drawing.Size(523, 467)
self._dataGridView1.TabIndex = 0
self._dataGridView1.DataBindingComplete += self.setRowNumber
# Initialize Form
self.ClientSize = System.Drawing.Size(547, 512)
self.Controls.Add(self._dataGridView1)
self.Name = "Form27"
self.Text = self.title
self._dataGridView1.EndInit()
self.ResumeLayout(False)
def setRowNumber(self, sender, e):
"""
Set the row numbers for each row in the DataGridView.
Args:
sender: The sender object.
e: The event arguments.
"""
# Add index to each row
for row in sender.Rows:
row.HeaderCell.Value = System.String.Format("{0}", row.Index)
class CustomDataTable(DataTable):
"""A custom DataTable class with additional functionalities"""
# FOR PYTHON 2
#def __new__(cls):
# return super(DataTable, cls).__new__(cls)
#
# OR
# FOR PYTHON 3 (Ironpython or PythonNet)
def __init__(self):
super().__init__()
def __repr__(self):
lst_size = []
for col in self.Columns:
max_size_txt = max([len(row[col]) for row in self.Rows])
lst_size.append(max_size_txt)
value = '\t'.join(['{0:{1}}'.format(col.ColumnName, lst_size[idx]) for idx, col in enumerate(list(self.Columns))]) + "\n"
value += '\n'.join(['\t'.join(['{0:{1}}'.format(x, lst_size[idx]) for idx, x in enumerate(r.ItemArray)]) for r in self.Rows])
return value
@staticmethod
def FromCsvFile(fullPathCsv):
"""
Create a CustomDataTable instance from a CSV file.
Args:
fullPathCsv (str): The full path to the CSV file.
Returns:
CustomDataTable: The created CustomDataTable instance.
"""
fileName = System.IO.Path.GetFileNameWithoutExtension(fullPathCsv)
data = []
with open(fullPathCsv, newline='') as csvfile:
reader = csv.reader(csvfile, delimiter=',')
data.extend([row for row in reader])
header_array = data.pop(0)
dt = CustomDataTable()
dt.TableName = fileName
# Create columns
for item, value in zip(header_array, data[0]):
if isinstance(value, (str, System.String)) and value.isdigit():
type_value = System.Int32
elif isinstance(value, (str, System.String)) and re.match(r"\d+[\.,]\d+$", value) is not None:
type_value = System.Double
else:
type_value = type(value)
dt.Columns.Add(item, type_value)
# Add rows
for sublst_values in data:
a = Array.CreateInstance(System.Object, len(sublst_values))
for i, val in enumerate(sublst_values):
a[i] = val
dt.Rows.Add(a)
return dt
def GroupSumByField(self, name_field):
"""
Group the DataTable by a field and calculate the sum for numeric columns.
Args:
name_field (str): The name of the field to group by.
Returns:
CustomDataTable: The new CustomDataTable with grouped and summed values.
"""
def SumFunc(g):
row = self.NewRow()
row[name_field] = g.Key
for col in self.Columns:
if col.ColumnName != name_field:
if any(col.DataType == typ for typ in [System.Int32, System.Int64, System.Double, System.Numerics.BigInteger, int, float]):
row[col.ColumnName] = sum([r[col.ColumnName] for r in g])
return row
new_dt = CustomDataTable()
for col in self.Columns:
new_dt.Columns.Add(col.ColumnName, col.DataType)
List[DataRow](self.AsEnumerable().GroupBy(lambda g : g[name_field]).Select(lambda g : SumFunc(g))).CopyToDataTable(new_dt, LoadOption.Upsert)
return new_dt
def Show(self):
"""Display the DataTable in a FormDataTable instance"""
objForm = FormDataTable(self)
objForm.ShowDialog()
objForm.Dispose()
def ColumnValues(self, columnName):
"""
Get an array of column values for a given column name.
Args:
columnName (str): The name of the column.
Returns:
Array: An array of column values.
"""
return self.AsEnumerable().Select(lambda s : s[columnName]).ToArray[System.Object]()
csv_file_path = IN[0]
dt = CustomDataTable.FromCsvFile(csv_file_path)
dt.Show()
ddt = dt.GroupSumByField("year")
ddt.Show()
OUT = ddt.ColumnValues("year"), ddt.ColumnValues("winner")
0 commentaires:
Enregistrer un commentaire