Demande pour code python aille 10 million de fois plus vite

Lilie3887 - Modifié le 4 févr. 2025 à 15:32
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 - 12 févr. 2025 à 18:31

Bonjour,

Modération : voir #9 pour voir ce message en version détaillée

J'aurais besoin d'aide pour que le code ci-dessous aille beaucoup beaucoup plus vite, car j'ai plus de 4 millions de fichiers à exécuter avec ce code. Si je le laisse tel qu'il est j'en aurais pour au moins une à 2 semaines d'exécution... alors que je dois l'utiliser toutes les semaines.

Le code fait une recherche par rapport à mon 1er tableau et met les valeurs dans mes fichiers, fait la somme de ma recherche, supprime les valeurs de ma recherche et place la ligne ou il y a la somme où il doit être placé.

Ensuite, il fait la même chose pour un 2ème tableau et idem pour un 3ème tableau, et pour un 4ème tableau, ça va jusqu'à 10 tableau.
S'il vous plaît, c'est complexe pour moi donc si vous pouvez m'aider.

Voici le code:

import pandas as pd
from openpyxl import load_workbook

# Définition des chemins
dossier_source = "D:/PYTHON/VALEUR REMPLACER ZIP"
fichier_rebase = "D:/PYTHON/REBASE.xlsx"

# Chargement des valeurs de REBASE.xlsx
wb_rebase = load_workbook(fichier_rebase, data_only=True)
ws_rebase = wb_rebase.active

# Extraction des valeurs des **Tableaux 1 (A:B), 2 (D:E), 3 (G:H) et 4 (J:K)**
tableau_1 = pd.DataFrame(
    ws_rebase.iter_rows(
        min_row=1, max_row=273,
        min_col=1, max_col=2,
        values_only=True
    ),
    columns=["Clé", "Valeur"]
)
tableau_2 = pd.DataFrame(
    ws_rebase.iter_rows(
        min_row=1, max_row=273,
        min_col=4, max_col=5,
        values_only=True
    ),
    columns=["Clé", "Valeur"]
)
tableau_3 = pd.DataFrame(
    ws_rebase.iter_rows(
        min_row=1, max_row=273,
        min_col=7, max_col=8,
        values_only=True
    ),
    columns=["Clé", "Valeur"]
)
tableau_4 = pd.DataFrame(
    ws_rebase.iter_rows(
        min_row=1, max_row=273,
        min_col=10, max_col=11,
        values_only=True
    ),
    columns=["Clé", "Valeur"]
)

# Traitement des fichiers dans le dossier source
for fichier in os.listdir(dossier_source):
    if fichier.endswith(".xlsx"):
        chemin_fichier = os.path.join(dossier_source, fichier)

# Charger le fichier Excel
wb = load_workbook(chemin_fichier)
ws = wb.active

# ???? Identifier la dernière ligne contenant des données (entre 1 et 19)
last_data_row = max(
    [
        i
        for i in range(1, 20)
        if any(ws.cell(row=i, column=j).value
        for j in range(2, ws.max_column + 1))
    ],
    default=1
)

### ✅ Étape 1 : Remplacement avec le **Tableau 1 (A:B)**
ligne_depart = 21
if any(
    ws.cell(row=21, column=j).value
    for j in range(2, ws.max_column + 1)
):
ligne_depart = max(
    [
        i
        for i in range(21, ws.max_row + 1)
        if any(ws.cell(row=i, column=j).value
        for j in range(2, ws.max_column + 1)
    )],
    default=20
) + 2
ligne_debut_bloc_1 = ligne_depart

# **Ajout des valeurs du Tableau 1**
for col in range(2, ws.max_column + 1):
    for row in range(1, last_data_row + 1):
        valeur_originale = ws.cell(row=row, column=col).value
        if valeur_originale:
            valeur_remplacee = tableau_1.loc[tableau_1["Clé"] == valeur_originale, "Valeur"]
        if not valeur_remplacee.empty:
            ws.cell(
                row=ligne_depart + row - 1,
                column=col,
                value=valeur_remplacee.values[0]
            )
        # ???? Calcul de la somme du **Tableau 1**
        ligne_somme_1 = ligne_depart + last_data_row + 1
        somme_bloc_1 = [
            sum(ws.cell(row=row, column=col).value or 0
            for row in range(ligne_debut_bloc_1, ligne_somme_1)
            if isinstance(ws.cell(row=row, column=col).value, (int, float)))
            for col in range(2, ws.max_column + 1)
        ]

# **Suppression du bloc 1 et insertion des sommes à la ligne 21**
for row in range(ligne_debut_bloc_1, ligne_somme_1):
    for col in range(2, ws.max_column + 1):
        ws.cell(row=row, column=col, value=None)
        ws.delete_rows(ligne_debut_bloc_1, ligne_somme_1 - ligne_debut_bloc_1)
        ws.insert_rows(21)
for col_idx, somme in enumerate(somme_bloc_1, start=2):
    ws.cell(row=21, column=col_idx, value=somme)

### Étape 4 : Remplacement avec le **Tableau 4 (J:K)**
ligne_depart = max(i
    [
        i
        for i in range(21, ws.max_row + 1)
        if any(ws.cell(row=i, column=j).value
        for j in range(2, ws.max_column + 1))
    ],
    default=20
) + 2
ligne_debut_bloc_4 = ligne_depart

# **Ajout des valeurs du Tableau 4**
for col in range(2, ws.max_column + 1):
    for row in range(1, last_data_row + 1):
        valeur_originale = ws.cell(row=row, column=col).value
        if valeur_originale:
            valeur_remplacee = tableau_4.loc[tableau_4["Clé"] == valeur_originale, "Valeur"]
        if not valeur_remplacee.empty:
            ws.cell(
                row=ligne_depart + row - 1,
                column=col,
                value=valeur_remplacee.values[0]
            )
        # Calcul de la somme du **Tableau 4**
        ligne_somme_4 = ligne_depart + last_data_row + 1
        somme_bloc_4 = [
            sum(ws.cell(row=row, column=col).value or 0
            for row in range(ligne_debut_bloc_4, ligne_somme_4)
            if isinstance(ws.cell(row=row, column=col).value, (int, float)))
            for col in range(2, ws.max_column + 1)
        ]

# **Suppression du bloc 4 et insertion des sommes à la ligne 24**
for row in range(ligne_debut_bloc_4, ligne_somme_4):
    for col in range(2, ws.max_column + 1):
        ws.cell(row=row, column=col, value=None)
        ws.delete_rows(ligne_debut_bloc_4, ligne_somme_4 - ligne_debut_bloc_4)
        ws.insert_rows(24)
for col_idx, somme in enumerate(somme_bloc_4, start=2):
    ws.cell(row=24, column=col_idx, value=somme)
    wb.save(chemin_fichier)
print(f"{fichier} mis à jour avec succès.")
print("✔ Tous les fichiers ont été traités correctement.")

Modération : remise en forme du message (merci de vérifier que le code a été correctement indenté)

A voir également:

8 réponses

mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828
Modifié le 5 févr. 2025 à 22:39

Bonjour,

En réponse à #20, voici un début de code que je te propose et qui repart de #12.

Proposition de code

#!/usr/bin/env python3

import argparse
import pandas as pd
from pathlib import Path

class DfRebase(pd.DataFrame):
    """
    The :py:class:`DfRebase` class is the dataframe
    used to wrap a rebase Excel file.
    """
    def __init__(self, filename: Path):
        """
        Constructor.

        Args:
            filename (Path): Path of the input rebase Excel file.

        Example:
            >>> df_rebase = DfRebase("OBfoz18rz4r_REBASE.xlsx")
        """
        df = pd.read_excel(filename, header=None)
        cols = df.head(1).dropna(axis=1).columns
        columns = df[cols].values[0]
        data = df[2:][cols + 1].dropna(axis=0)
        index = df[0][data.index]
        super().__init__(
            data=data.values,
            columns=columns,
            index=index
        )

class DfSource(pd.DataFrame):
    """
    The :py:class:`DfSource` class is the dataFrame
    used to wrap a source Excel file.
    """
    def __init__(self, filename: Path):
        """
        Constructor.

        Args:
            filename (Path): Path of the input source Excel file.

        Example:
            >>> df_source = DfSource("OBfoAYcDp4r_Classeur1.xlsx")
        """
        data = pd.read_excel(filename, header=None).dropna(axis=1)
        index = data[1].values
        super().__init__(
            data=data.values,
            index=index,
        )

    def compute(self, df_rebase: DfRebase, row_start):
        print("DbRebase:")
        print(df_rebase)
        print(df_rebase.index)
        print("DbSource:")
        print(self)
        print(self.index)
        print("Index:")
        for index in self.index:
            row = df_rebase.loc[index].values
            print(f"{index:25s} {row}")
        print("Results")
        df_result = df_rebase.loc[self.index].sum(axis=1)
        print(df_result)
        return df_result

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--rebase-filename",
        required=False,
        type=Path,
        default="OBfoz18rz4r_REBASE.xlsx",
        help="Path to the input xlsx rebase file."
    )
    parser.add_argument(
        "--source-filename",
        required=False,
        type=Path,
        default="OBfoAYcDp4r_Classeur1.xlsx",
        help="Path to the input xlsx source file."
    )
    args = parser.parse_args()
    assert args.rebase_filename.exists(), args.rebase_filename
    df_rebase = DfRebase(args.rebase_filename)
    assert args.source_filename.exists(), args.source_filename
    df_source = DfSource(args.source_filename)
    df_results = df_source.compute(df_rebase, row_start=21)

if __name__ == "__main__":
    main()

Explications

  • On définit une classe de dataframe par type de fichier (donc source et rebase)
  • On commence par remettre en forme la dataframe "rebase" dans DfRebase.__init__:
    • on récupère les noms de colonnes à partir des cellules A1, D1, ...
    • on supprime les lignes vides
    • on indexe les lignes par les valeurs de la colonne A (qui sont répliquées dans les colonnes D, G, ....). Cette indexation sera exploitée ultérieurement pour faire la jointure avec les données d'une DfSource.
    • les valeurs associés à un nom de colonne sont décalées d'une colonne ce qui oblige à décaler les index de colonnes
  • On fait le même travail pour remettre en forme une dataframe "source" dans DfSource.__init__.
    • on indexe les lignes par les valeurs de la colonne A (qui coïncident avec les index définis dans DfRebase)
    • je n'ai pas l'impression que les valeurs aient d'utilité (dans l'exemple que tu donnes, toutes les valeurs d'une ligne sont identiques)
  • On définit la méthode DfSource.compute qui permet de croiser les données. Pour être honnête je n'ai pas compris ton code entre les lignes 58 et 100 (trop compliqué à lire) mais c'est dans cette méthode qu'il faut le traduire en pandas. J'ai donc mis un code arbitraire :
    • D'abord, je montre accéder aux différentes valeurs "intéressantes" dans chacune des deux dataframe.
    • Ensuite, pour chaque identifiant de ligne (colonne A de DfSource) comment récupérer les valeurs correspondantes de DfRebase et comment les sommer.
    • Si ce n'est pas ce que tu veux calculer, merci de clarifier, avec ces nouvelles notations, ce que tu souhaites calculer.

Résultat

python script.py --rebase-filename OBfoz18rz4r_REBASE.xlsx --source-filename OBfoAYcDp4r_Classeur1.xlsx
DbRebase:
                  2024-09-20  2024-09-19  2024-09-18  2024-09-17  2024-09-16  2024-09-15  2024-09-14  2024-09-13
0                                                                                                               
2024 :R1 PIERRES         1.0         2.0         3.0         1.0         1.0         2.0         3.0         1.0
2024 :R2 PIERRES         1.0         2.0         3.0         2.0         1.0         2.0         3.0         2.0
2024 :R3 PIERRES         1.0         2.0         3.0         3.0         1.0         2.0         3.0         3.0
2024 :R4 PIERRES         1.0         2.0         3.0         4.0         1.0         2.0         3.0         4.0
2024 :R5 PIERRES         1.0         2.0         3.0         5.0         1.0         2.0         3.0         5.0
...                      ...         ...         ...         ...         ...         ...         ...         ...
2021 :GOMME              1.0         2.0         3.0        63.0         1.0         2.0         3.0        63.0
2021 :AGRAFES            1.0         2.0         3.0        64.0         1.0         2.0         3.0        64.0
2021 :POST IT            1.0         2.0         3.0        65.0         1.0         2.0         3.0        65.0
2021 :MARQUEUR           1.0         2.0         3.0        66.0         1.0         2.0         3.0        66.0
2021 :CORRECTEUR         1.0         2.0         3.0        67.0         1.0         2.0         3.0        67.0

[268 rows x 8 columns]
Index(['2024 :R1 PIERRES', '2024 :R2 PIERRES', '2024 :R3 PIERRES',
       '2024 :R4 PIERRES', '2024 :R5 PIERRES', '2024 :R1 STYLO BLEU',
       '2024 :R2 STYLO BLEU', '2024 :R3 STYLO BLEU', '2024 :R4 STYLO BLEU',
       '2024 :R5 STYLO BLEU',
       ...
       '2021 :R3 SURLIGNEUR', '2021 :R4 SURLIGNEUR', '2021 :R5 SURLIGNEUR',
       '2021 :GOMME', '2021 :OTE AGRAFE', '2021 :GOMME', '2021 :AGRAFES',
       '2021 :POST IT', '2021 :MARQUEUR', '2021 :CORRECTEUR'],
      dtype='object', name=0, length=268)
DbSource:
                                            0                       1                       2   ...                      17                      18                      19
2024 :R1 PIERRES              2024 :R1 PIERRES        2024 :R1 PIERRES        2024 :R1 PIERRES  ...        2024 :R1 PIERRES        2024 :R1 PIERRES        2024 :R1 PIERRES
2024 :R1 STYLO BLEU        2024 :R1 STYLO BLEU     2024 :R1 STYLO BLEU     2024 :R1 STYLO BLEU  ...     2024 :R1 STYLO BLEU     2024 :R1 STYLO BLEU     2024 :R1 STYLO BLEU
2024 :R1 CRAYONS              2024 :R1 CRAYONS        2024 :R1 CRAYONS        2024 :R1 CRAYONS  ...        2024 :R1 CRAYONS        2024 :R1 CRAYONS        2024 :R1 CRAYONS
2024 :R1 STYLO VERT        2024 :R1 STYLO VERT     2024 :R1 STYLO VERT     2024 :R1 STYLO VERT  ...     2024 :R1 STYLO VERT     2024 :R1 STYLO VERT     2024 :R1 STYLO VERT
2024 :R1 STYLO ROUGE      2024 :R1 STYLO ROUGE    2024 :R1 STYLO ROUGE    2024 :R1 STYLO ROUGE  ...    2024 :R1 STYLO ROUGE    2024 :R1 STYLO ROUGE    2024 :R1 STYLO ROUGE
2024 :R1 STYLO NOIR        2024 :R1 STYLO NOIR     2024 :R1 STYLO NOIR     2024 :R1 STYLO NOIR  ...     2024 :R1 STYLO NOIR     2024 :R1 STYLO NOIR     2024 :R1 STYLO NOIR
2024 :R1 STYLO FUSCHIA  2024 :R1 STYLO FUSCHIA  2024 :R1 STYLO FUSCHIA  2024 :R1 STYLO FUSCHIA  ...  2024 :R1 STYLO FUSCHIA  2024 :R1 STYLO FUSCHIA  2024 :R1 STYLO FUSCHIA
2024 :R1 STYLO JAUNE      2024 :R1 STYLO JAUNE    2024 :R1 STYLO JAUNE    2024 :R1 STYLO JAUNE  ...    2024 :R4 STYLO JAUNE    2024 :R4 STYLO JAUNE    2024 :R4 STYLO JAUNE
2024 :R1 STYLO ORANGE    2024 :R1 STYLO ORANGE   2024 :R2 STYLO ORANGE   2024 :R3 STYLO ORANGE  ...   2024 :R3 STYLO ORANGE   2024 :R4 STYLO ORANGE   2024 :R5 STYLO ORANGE

[9 rows x 20 columns]
Index(['2024 :R1 PIERRES', '2024 :R1 STYLO BLEU', '2024 :R1 CRAYONS',
       '2024 :R1 STYLO VERT', '2024 :R1 STYLO ROUGE', '2024 :R1 STYLO NOIR',
       '2024 :R1 STYLO FUSCHIA', '2024 :R1 STYLO JAUNE',
       '2024 :R1 STYLO ORANGE'],
      dtype='object')
Index:
2024 :R1 PIERRES          [1. 2. 3. 1. 1. 2. 3. 1.]
2024 :R1 STYLO BLEU       [1. 2. 3. 6. 1. 2. 3. 6.]
2024 :R1 CRAYONS          [ 1.  2.  3. 11.  1.  2.  3. 11.]
2024 :R1 STYLO VERT       [ 1.  2.  3. 16.  1.  2.  3. 16.]
2024 :R1 STYLO ROUGE      [ 1.  2.  3. 21.  1.  2.  3. 21.]
2024 :R1 STYLO NOIR       [ 1.  2.  3. 26.  1.  2.  3. 26.]
2024 :R1 STYLO FUSCHIA    [ 1.  2.  3. 31.  1.  2.  3. 31.]
2024 :R1 STYLO JAUNE      [ 1.  2.  3. 36.  1.  2.  3. 36.]
2024 :R1 STYLO ORANGE     [ 1.  2.  3. 41.  1.  2.  3. 41.]
Results
2024 :R1 PIERRES          14.0
2024 :R1 STYLO BLEU       24.0
2024 :R1 CRAYONS          34.0
2024 :R1 STYLO VERT       44.0
2024 :R1 STYLO ROUGE      54.0
2024 :R1 STYLO NOIR       64.0
2024 :R1 STYLO FUSCHIA    74.0
2024 :R1 STYLO JAUNE      84.0
2024 :R1 STYLO ORANGE     94.0
dtype: float64

Bonne chance

2
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
6 févr. 2025 à 01:20

merci, mais ça m'a donné des erreurs sniiiiff

0
luckydu43 Messages postés 4077 Date d'inscription vendredi 9 janvier 2015 Statut Membre Dernière intervention 13 février 2025 1 003 > Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
6 févr. 2025 à 09:11

Bonjour,

Je ne sais pas quelle réponse tu attends de @mamiemando StatutModérateur ou de @yg_be StatutContributeur suite à ta réponse. Tu as là les développeurs parmi (s'ils ne sont) les plus compétents du forum (c'est bien pour ça que je n'interfère pas), garde-les sous la main, fais-les revenir !

mamiemando vient de te montrer que dans son environnement, avec les données de test que tu as fourni, ça a fonctionné.

Partage ton erreur, fournis le fichier traité au moment de l'erreur, maximise les détails de ta configuration (version de Python et de pandas utilisés par ex) qu'on puisse reproduire.

  • relis régulièrement tout ce qui a été posté pour ne pas perdre de vue les différentes solutions proposées
  • essaie de comprendre l'erreur
  • de corriger le problème
  • poste tes avancées voire, régulièrement, ta version actuelle du code.
  • ne t'arrêtes pas parce que les bénévoles sur ccm n'ont pas pris le temps de répondre à un "ça marche pa" sans contexte

Et bonus, alerte la hiérarchie sur les risques concernant ce projet, je doute que tu sois seul(e) à bord au vu de la volumétrie. Que je sache, aujourd'hui, c'est encore en échec -_-''

1
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828 > luckydu43 Messages postés 4077 Date d'inscription vendredi 9 janvier 2015 Statut Membre Dernière intervention 13 février 2025
10 févr. 2025 à 14:13

@Lilie3887 StatutMembre quelle est ton erreur ?

0
yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025 Ambassadeur 1 568
5 févr. 2025 à 12:21

une suggestion:

import multiprocessing
import os
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle

def m(x,mod):
        # ???? Définition des chemins
    dossier_source = "D:/PYTHON/VALEUR REMPLACER ZIP"
    fichier_rebase = "D:/PYTHON/REBASE.xlsx"

    # ???? Chargement des valeurs de REBASE.xlsx en RAM
    wb_rebase = load_workbook(fichier_rebase, data_only=True)
    ws_rebase = wb_rebase.active

    # ???? Extraction des dates des tableaux (Ligne 1 de REBASE.xlsx) avec formatage JJ/MM/AAAA
    dates_tableaux = {
        1: ws_rebase.cell(row=1, column=1).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=1).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=1).value,
        2: ws_rebase.cell(row=1, column=4).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=4).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=4).value,
        3: ws_rebase.cell(row=1, column=7).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=7).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=7).value
    }

    # ???? Stockage des tableaux sous forme de dictionnaires pour un accès rapide
    tableaux = {
        1: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=1, max_col=2, values_only=True) if row[0] is not None},
        2: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=4, max_col=5, values_only=True) if row[0] is not None},
        3: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=7, max_col=8, values_only=True) if row[0] is not None}
    }
    wb_rebase.close()
    # ???? Traitement des fichiers dans le dossier source
    for fichier in os.listdir(dossier_source):
        if fichier.endswith(".xlsx") and hash(fichier)%mod==x:
            chemin_fichier = os.path.join(dossier_source, fichier)

            # ???? Charger le fichier Excel une seule fois
            wb = load_workbook(chemin_fichier)
            ws = wb.active

            # ???? **Appliquer le format "Date courte" à toute la colonne A**
            date_style = NamedStyle(name="date_style")
            date_style.number_format = "DD/MM/YYYY"

            for row in range(1, ws.max_row + 1):
                cell = ws.cell(row=row, column=1)
                cell.style = date_style  # Appliquer le style à la colonne A

            # ???? Identifier la dernière ligne contenant des données (entre 1 et 19)
            last_data_row = max(
                (i for i in range(1, 20) if any(ws.cell(row=i, column=j).value for j in range(2, ws.max_column + 1))),
                default=1
            )

            # ???? Récupérer toutes les cellules en RAM pour un traitement rapide
            data = [[ws.cell(row=row, column=col).value for col in range(2, ws.max_column + 1)] for row in range(1, last_data_row + 1)]

            ligne_depart = 21  # ???? Début d'écriture des valeurs

            for idx, mapping in tableaux.items():
                # ???? Appliquer les remplacements **sans modifier les valeurs d'origine**
                data_remplacee = [[mapping.get(val, val) for val in row] for row in data]

                # ???? Trouver la prochaine ligne vide **sans écraser les données**
                while any(ws.cell(row=ligne_depart, column=j).value for j in range(2, ws.max_column + 1)):
                    ligne_depart += 1
                ligne_debut_bloc = ligne_depart

                # ???? **Écriture optimisée des nouvelles valeurs**
                for i, row in enumerate(data_remplacee):
                    for j, val in enumerate(row, start=2):
                        ws.cell(row=ligne_depart + i, column=j, value=val)

                # ???? Calcul de la somme
                somme_bloc = [sum(cell for cell in col if isinstance(cell, (int, float))) for col in zip(*data_remplacee)]

                # ???? Trouver une ligne vide **sans écraser de données**
                ligne_somme = ligne_depart + last_data_row
                while any(ws.cell(row=ligne_somme, column=j).value for j in range(2, ws.max_column + 1)):
                    ligne_somme += 1

                # ???? **Suppression correcte du bloc**
                ws.delete_rows(ligne_debut_bloc, last_data_row)

                # ???? **Écriture optimisée des sommes**
                for j, somme in enumerate(somme_bloc, start=2):
                    ws.cell(row=ligne_debut_bloc, column=j, value=somme)

                # ✅ **Ajout de la date formatée en colonne A**
                date_tableau = dates_tableaux[idx]  # Date associée au tableau
                cell_date = ws.cell(row=ligne_debut_bloc, column=1, value=date_tableau)
                cell_date.style = date_style  # Appliquer le style "Date courte"

                # ???? Mise à jour de la ligne de départ pour le bloc suivant
                ligne_depart = ligne_debut_bloc + 1

            # ???? **Sauvegarde du fichier après toutes les opérations**
            wb.save(chemin_fichier)
            print(f"✅ {fichier} mis à jour avec succès.")

if __name__ == '__main__':
    nproc=10
    prc=[]
    for i in range(nproc):
        p=multiprocessing.Process(target=m, args=(i,nproc))
        p.start()
        prc.append(p)
    for pp in prc:
        pp.join()
    print("✔ Tous les fichiers ont été traités correctement.")
1
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
5 févr. 2025 à 14:52

Merci pour ton code, en l'exécutant j'ai eu ce message d'erreur:

PS C:\Users\dalin> py "D:/PYTHON/mon_script.py"
>>
Process Process-5:
Traceback (most recent call last):
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\PYTHON\mon_script.py", line 45, in m
    cell.style = date_style  # Appliquer le style à la colonne A
    ^^^^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\styleable.py", line 77, in __set__
    instance.parent.parent.add_named_style(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\workbook\workbook.py", line 347, in add_named_style
    self._named_styles.append(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\named_styles.py", line 186, in append
    raise ValueError("""Style {0} exists already""".format(style.name))
ValueError: Style date_style exists already
Process Process-8:
Traceback (most recent call last):
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\PYTHON\mon_script.py", line 45, in m
    cell.style = date_style  # Appliquer le style à la colonne A
    ^^^^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\styleable.py", line 77, in __set__
    instance.parent.parent.add_named_style(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\workbook\workbook.py", line 347, in add_named_style
    self._named_styles.append(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\named_styles.py", line 186, in append
    raise ValueError("""Style {0} exists already""".format(style.name))
ValueError: Style date_style exists already
Process Process-7:
Traceback (most recent call last):
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 313, in _bootstrap
    self.run()
    ~~~~~~~~^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\multiprocessing\process.py", line 108, in run
    self._target(*self._args, **self._kwargs)
    ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "D:\PYTHON\mon_script.py", line 45, in m
    cell.style = date_style  # Appliquer le style à la colonne A
    ^^^^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\styleable.py", line 77, in __set__
    instance.parent.parent.add_named_style(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\workbook\workbook.py", line 347, in add_named_style
    self._named_styles.append(style)
    ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^
  File "C:\Users\dalin\AppData\Local\Programs\Python\Python313\Lib\site-packages\openpyxl\styles\named_styles.py", line 186, in append
    raise ValueError("""Style {0} exists already""".format(style.name))
ValueError: Style date_style exists already
✔ Tous les fichiers ont été traités correctement.
PS C:\Users\dalin>
0

Bonjour,

Quel galamatias !

on y verrait plus clair si tu affichais ton code avec les balises de code
il faut cliquer cette icone:

Visuellement, ça doit ressembler à ça:

for k in range(10):
    print(k)
0
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
3 févr. 2025 à 16:50

D'accord merci, je vais refaire une autre discussion 

0
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828
3 févr. 2025 à 12:49

Bonjour,

J'ai tenté de remettre ton code en forme mais vu qu'il n'était pas indenté, j'espère qu'il est toujours tel que tu souhaitais le partager. Merci de corriger le message initial ou de redonner le code comme expliqué par Phil_1857 dans #1 si ça n'est pas le cas.

Plusieurs choses :

  • J'ai l'impression que beaucoup de code est répliqué pour les 4 tableaux. Pourquoi ne pas définir une fonction que tu appellerais pour chaque tableau ? Cela améliorerais la lisibilité et simplifierait la maintenance du code (corrections de bug, optimisations...)
  • Sans partager un jeu de données minimale (et le résultat correspondant), il n'est pas évident de reproduire ce que fait ton code ni de comprendre ce qu'il est sensé faire.
  • Essaye d'indenter ton code pour ne pas avoir des lignes trop longues (en python, du moment que tu es entre des parenthèses, accolades ou crochets, tu peux passer à la ligne).

À ce stade je ne comprends pas ce que fait ton programme donc je ne peux pas te dire comment réécrire ton code.

Je vois que tu utilises pandas. C'est très bien c'est un module efficace pour traiter de gros volumes de données. Mais tel que tu as écrit ton code, tu n'exploites pas la "vectorisation" offerte par pandas, et donc ton programme est considérablement ralenti. Pars du principe qu'itérer sur une DataFrame avec des boucles for (même dans des comprehension listes) sera beaucoup plus lent qu'utiliser les méthodes exposées par la classe DataFrame.

Prenons un exemple : tu calcules des sommes en itérant case par case. Tu devrais plutôt utiliser la méthode pd.DataFrame.sum. Pour ne sommer que ce que tu veux, il faut définir un masque qui permet de ne conserver que les lignes adéquates (ce qui induit une pd.Series). Certains exemples dans ce lien montrent comment procéder.

import pandas as pd

# Create a sample DataFrame
df = pd.DataFrame({
    'Date': ['2023-01-01', '2023-01-02', '2023-01-03'],
    'Sales': [100, 250, 175]}
)

# Sum the sales for the specific date '2023-01-02'
sum_sales = df.loc[df['Date'] == '2023-01-02', 'Sales'].sum()
print(sum_sales)

Comme le montre l'exemple ci-dessus, tu peux masquer certaines colonnes et lignes selon un prédicat arbitraire. Il est possible d'utiliser de combiner avec & (et logique), | (ou logique) des masques ou de les renverser avec ~ (non logique), mais dans ce cas veille à mettre chaque masque entre parenthèses sinon pandas lèveras une exception.

Bonne chance

0
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
3 févr. 2025 à 16:52

D'accord merci, je vais refaire la discussion pour y voir plus clair

0
yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025 1 568 > Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
3 févr. 2025 à 20:18

bonjour,

il est préférable de continuer la discussion ici.

Veille à partager des données de test.  L'efficacité du programme dépend des données.

0
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025 > yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025
3 févr. 2025 à 21:18

ah mince j'ai ouvert une autre discussion ou j'ai mieux rédigez ma demande, et j'ai les liens du fichier aussi. je vais mettre ma demande en commentaire et supprimer ce que j'ai fait

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
bazfile Messages postés 57392 Date d'inscription samedi 29 décembre 2012 Statut Modérateur, Contributeur sécurité Dernière intervention 15 février 2025 19 482
Modifié le 3 févr. 2025 à 17:23

Bonjour.

Voir ici


0
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
Modifié le 4 févr. 2025 à 15:31

Je refais ma demande avec plus de renseignements.

J'aurais besoin d'aide pour que le code ci-dessous aille beaucoup beaucoup plus vite, car j'ai plus de 4 millions de fichier à exécuter avec ce code et si je le laisse tel qu'il est j'en aurais pour au moins une à 2 semaines d'exécution. Alors que je dois l'utiliser toutes les semaines. Le code fais une recherche du fichier REBASE et transmets les valeurs dans le dossier D:\PYTHON\VALEUR REMPLACER ZIP, Voici les liens des fichiers:

Je vais vous détailler ce que fait exactement le code:

Pour le 1er tableau du fichier REBASE

  • Le code cherche les valeurs correspondantes du fichier REBASE pour les inscrire dans les fichiers du dossier
    • il ne les affiche qu'à partir de la ligne 21 que si dans la ligne 21 il n'y a pas de donnée,
    • s'il y a des données, il les affiche en dessous des données mais laisse une ligne vide entre les données existantes à partir de la ligne 21 et les valeurs qui vont être inscrites.
  • Dès que les valeurs ont été affichées il fait la somme que du bloc et par colonne, et il affiche le résultat en dessous du bloc avec une ligne vide entre le bloc et le résultat.
  • Dès que la ligne de la somme est affichée, il supprime le bloc de valeur qu'il vient de rajouté (pour ne pas que le fichier soit trop lourd car il peut y avoir jusqu'à 19 noms à remplacer).
  • Dès que le bloc est supprimé, il prend la ligne des sommes et il la déplace à la ligne 21. Mais attention, s'il y a des données à la ligne 21 il décale les données de la ligne 21 à la ligne 22 et il marque la date qui se trouve dans la cellule A1 du fichier REBASE, dans la colonne A21 des fichiers qui se trouve dans le dossier.

Pour le 2ème tableau du fichier REBASE

Le code fait exactement la même chose que pour le 1er tableau, sauf pour la dernière étape, c'est-à-dire que dès que le bloc est supprimé, le code prend la ligne des sommes et il la déplace à la ligne 22.

Mais attention, s'il y a des données à la ligne 22, il décale les données de la ligne 22 à la ligne 23 et il marque la date qui se trouve dans la cellule D1 du fichier REBASE, dans la colonne A22 des fichiers qui se trouve dans le dossier.

Pour le 3ème tableau du fichier REBASE

Le code fait exactement la même chose que pour le 1er tableau et 2ème tableau, sauf pour la dernière étape, c'est-à-dire que dès que le bloc est supprimé, le code prend la ligne des sommes et il la déplace à la ligne 23.

Mais attention, s'il y a des données à la ligne 23, il décale les données de la ligne 23 à la ligne 24 et il marque la date qui se trouve dans la cellule G1 du fichier REBASE, dans la colonne A23 des fichiers qui se trouve dans le dossier.

Je dois faire ce code pour 4, 5, 6 ,7... jusqu'au 10ème tableaux.
C'est pour ça que j'ai besoin de votre aide car c'est trop complexe pour moi, en tout cas je vous remercie et voici le code que j'ai:

import os
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle

# ???? Définition des chemins
dossier_source = "D:/PYTHON/VALEUR REMPLACER ZIP"
fichier_rebase = "D:/PYTHON/REBASE.xlsx"

# ???? Chargement des valeurs de REBASE.xlsx en RAM
wb_rebase = load_workbook(fichier_rebase, data_only=True)
ws_rebase = wb_rebase.active

# ???? Extraction des dates des tableaux (Ligne 1 de REBASE.xlsx) avec formatage JJ/MM/AAAA
dates_tableaux = {
    1: ws_rebase.cell(row=1, column=1).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=1).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=1).value,
    2: ws_rebase.cell(row=1, column=4).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=4).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=4).value,
    3: ws_rebase.cell(row=1, column=7).value.strftime("%d/%m/%Y") if isinstance(ws_rebase.cell(row=1, column=7).value, (pd.Timestamp, str)) else ws_rebase.cell(row=1, column=7).value
}

# ???? Stockage des tableaux sous forme de dictionnaires pour un accès rapide
tableaux = {
    1: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=1, max_col=2, values_only=True) if row[0] is not None},
    2: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=4, max_col=5, values_only=True) if row[0] is not None},
    3: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=7, max_col=8, values_only=True) if row[0] is not None}
}

# ???? Traitement des fichiers dans le dossier source
for fichier in os.listdir(dossier_source):
    if fichier.endswith(".xlsx"):
        chemin_fichier = os.path.join(dossier_source, fichier)

        # ???? Charger le fichier Excel une seule fois
        wb = load_workbook(chemin_fichier)
        ws = wb.active

        # ???? **Appliquer le format "Date courte" à toute la colonne A**
        date_style = NamedStyle(name="date_style")
        date_style.number_format = "DD/MM/YYYY"

        for row in range(1, ws.max_row + 1):
            cell = ws.cell(row=row, column=1)
            cell.style = date_style  # Appliquer le style à la colonne A

        # ???? Identifier la dernière ligne contenant des données (entre 1 et 19)
        last_data_row = max(
            (i for i in range(1, 20) if any(ws.cell(row=i, column=j).value for j in range(2, ws.max_column + 1))),
            default=1
        )

        # ???? Récupérer toutes les cellules en RAM pour un traitement rapide
        data = [[ws.cell(row=row, column=col).value for col in range(2, ws.max_column + 1)] for row in range(1, last_data_row + 1)]

        ligne_depart = 21  # ???? Début d'écriture des valeurs

        for idx, mapping in tableaux.items():
            # ???? Appliquer les remplacements **sans modifier les valeurs d'origine**
            data_remplacee = [[mapping.get(val, val) for val in row] for row in data]

            # ???? Trouver la prochaine ligne vide **sans écraser les données**
            while any(ws.cell(row=ligne_depart, column=j).value for j in range(2, ws.max_column + 1)):
                ligne_depart += 1
            ligne_debut_bloc = ligne_depart

            # ???? **Écriture optimisée des nouvelles valeurs**
            for i, row in enumerate(data_remplacee):
                for j, val in enumerate(row, start=2):
                    ws.cell(row=ligne_depart + i, column=j, value=val)

            # ???? Calcul de la somme
            somme_bloc = [sum(cell for cell in col if isinstance(cell, (int, float))) for col in zip(*data_remplacee)]

            # ???? Trouver une ligne vide **sans écraser de données**
            ligne_somme = ligne_depart + last_data_row
            while any(ws.cell(row=ligne_somme, column=j).value for j in range(2, ws.max_column + 1)):
                ligne_somme += 1

            # ???? **Suppression correcte du bloc**
            ws.delete_rows(ligne_debut_bloc, last_data_row)

            # ???? **Écriture optimisée des sommes**
            for j, somme in enumerate(somme_bloc, start=2):
                ws.cell(row=ligne_debut_bloc, column=j, value=somme)

            # ✅ **Ajout de la date formatée en colonne A**
            date_tableau = dates_tableaux[idx]  # Date associée au tableau
            cell_date = ws.cell(row=ligne_debut_bloc, column=1, value=date_tableau)
            cell_date.style = date_style  # Appliquer le style "Date courte"

            # ???? Mise à jour de la ligne de départ pour le bloc suivant
            ligne_depart = ligne_debut_bloc + 1

        # ???? **Sauvegarde du fichier après toutes les opérations**
        wb.save(chemin_fichier)
        print(f"✅ {fichier} mis à jour avec succès.")

print("✔ Tous les fichiers ont été traités correctement.")
0

Salut, si tu veux de la performance, oublis python et fais ton programme en C ou autre langage compilé.

Et même en C, 4 millions de fichiers à traiter un à un, ça va être long, faudra forcément utiliser des threads ou autres moyens de traiter tes fichiers en parallèles...

1
yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025 1 568 > bouah
4 févr. 2025 à 10:06

Il suffit peut-être de faire tourner une dizaine de copies du programme, chaque copie traitant une partie des fichiers.

0
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828 > yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025
Modifié le 5 févr. 2025 à 18:24

Bonjour

  • @bouah StatutMembre tu peux parfaitement avoir un traitement performant en python avec pandas, car ce dernier est basé sur numpy qui enveloppe en python des primitives vectorisées écrites en C. Numpy (et donc pandas) est très performant à condition d'utiliser les primitives vectorisées. Dit autrement les boucles sont faites en C et on s'affranchit de la lenteur de python. De plus, écrire ce programme en C n'est pas forcément si aisé (il faut être capable d'ouvrir un fichier excel, etc...) et apprendre toute la syntaxe liée au C.
  • @yg_be StatutContributeur
    • Ta proposition c'est une manière de paralléliser le code de manière un peu "sale" si tu me passes l'expression.
      • Si le traitement d'un tableau est écrit sous la forme d'une fonction bien écrite et pensée pour s'adapter aux tableaux suivants, il est assez facile de paralléliser le code directement en python.
    • Quelque soit la manière de paralléliser
      • Il faut être vigilants concernant les accès concurrents n'entrent pas en collision.
      • Tu auras un facteur multiplicatif sur le nombre de CPU et donc un facteur multiplicatif bien inférieur au but recherché. Au contraire, vectoriser le code (où à l'extrême le réécrire en C) pourrait être bien plus efficace.
  • @Lilie3887 StatutMembre 
    • Merci pour les précisions. Toutefois les liens que tu partages demandent de s'authentifier (ce qui oblige à demander une autorisation et avoir un compte gmail, bref pas très pratique).
      • Exporte ta dataframe actuelle sous forme de liste avec la méthode tolist()
      • Réduis cette liste pour faire un exemple minimal reproductible. Éventuellement anonymise les données.
      • Puis reporte-nous la déclaration de la dataframe à partir de cette liste.
    • À ce stade, essaye de factoriser ton code. Tu le dis toi-même, les procédures d'un tableau à l'autres sont similaires. Cela veut dire que tu peux faire une fonction f prenant en paramètre la dataframe, la ligne de référence (21, 22, etc.), puis appeler cette fonction pour chaque couple (tableau, ligne de référence). Une fois ton code factorisé, reporte-le. À vue de nez le code doit ressembler à ça :
      #!/usr/bin/env python3
                  
      import argparse
      import pandas as pd
      from pathlib import Path
      
      def f(df: pd.DataFrame, row_ref: int):
          print(df, row_ref)
          # Utilise les primitives pandas adéquates à partir d'ici.
              
      def main(filename: Path):
          xlsb = pd.ExcelFile(filename)
          dfs = [
              xlsb.parse(sheet_name=sheet_name)
              for sheet_name in xlsb.sheet_names
          ]
          for (df, row_ref) in zip(dfs, range(21, 21 + len(dfs))):
              f(df, row_ref)
      
      if __name__ == "__main__":
          parser = argparse.ArgumentParser()
          parser.add_argument(
              "--rebase",
              required=True,
              type=Path,
              help="Path to the input xlsb file."
          )
          args = parser.parse_args()
          assert args.rebase.exists(), args.rebase
          main(args.rebase)
    • Ne reconstruis pas "à la main" le contenus des feuilles, c'est très coûteux et tu te prives de la force de pandas. Puis, essaye de t'appuyer sur les primitives pandas pour accélérer ton code.
    • Enfin, remplace la boucle for appelant la fonction f par ThreadPoolExecutor (voir ici) si tu veux paralléliser ton code.

Bonne chance

1
yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025 1 568 > mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025
4 févr. 2025 à 16:19

Je pense indispensable de paralléliser au niveau des 4 millions de fichiers à traiter, tout autant pour utiliser plusieurs processeurs que pour éviter que rien ne se passe lors des accés au disque.
Cela évite également toute collision concernant les accès concurrents.

Je préfère, par ailleurs, proposer une solution simple, à la portée du demandeur.

1
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828 > yg_be Messages postés 23468 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 14 février 2025
4 févr. 2025 à 16:31

Si chaque fil d'exécution s'applique sur des fichiers deux à deux distincts, oui. S'ils s'appliquent sur plusieurs feuilles d'un même fichier xlsb, non (accès concurrent au même fichier). Ensuite, il faut voir la taille des fichiers. Plus ils sont gros, plus il faut accepter d'utiliser pandas (ou changer de langage).

0
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025
4 févr. 2025 à 21:50

je vous avoue je suis très très nul en Python, il y a des termes que je ne maitrise pas et je n'ai jamais fait de formation de programmateur donc je me suis débrouillé avec ce que je pouvais. j'ai rajouté à mes codes qu'il fallait supprimer toute les données à  la ligne 41 et en dessous de la ligne 41. je vous envoie le code pour remplacer les données de 4 tableaux

import os
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle

# ???? Définition des chemins
dossier_source = "D:/PYTHON/VALEUR REMPLACER ZIP"
fichier_rebase = "D:/PYTHON/REBASE.xlsx"

# ???? Chargement des valeurs de REBASE.xlsx en RAM
wb_rebase = load_workbook(fichier_rebase, data_only=True)
ws_rebase = wb_rebase.active

# ???? Extraction des dates des tableaux (Ligne 1 de REBASE.xlsx)
dates_tableaux = {
    1: ws_rebase.cell(row=1, column=1).value,
    2: ws_rebase.cell(row=1, column=4).value,
    3: ws_rebase.cell(row=1, column=7).value,
    4: ws_rebase.cell(row=1, column=10).value
}

# ???? Stockage des tableaux sous forme de dictionnaires pour un accès rapide
tableaux = {
    i: {row[0]: row[1] for row in ws_rebase.iter_rows(min_row=2, max_row=273, min_col=(i-1)*3+1, max_col=(i-1)*3+2, values_only=True) if row[0] is not None}
    for i in range(1, 5)  # ???? 4 tableaux (de 1 à 4)
}

# ???? Traitement des fichiers dans le dossier source
for fichier in os.listdir(dossier_source):
    if fichier.endswith(".xlsx"):
        chemin_fichier = os.path.join(dossier_source, fichier)

        # ???? Charger le fichier Excel une seule fois
        wb = load_workbook(chemin_fichier)
        ws = wb.active

        # ???? Appliquer le format "Date courte" à toute la colonne A
        existing_styles = wb.named_styles  # Liste des styles existants

        if "date_style" not in existing_styles:
            date_style = NamedStyle(name="date_style")
            date_style.number_format = "DD/MM/YYYY"
            wb.add_named_style(date_style)
        else:
            date_style = "date_style"

        for row in range(1, ws.max_row + 1):
            cell = ws.cell(row=row, column=1)
            cell.style = date_style  # Appliquer le style à la colonne A

        # ???? Décaler toutes les lignes existantes à partir de la ligne 21 pour insérer les résultats des 4 tableaux
        ws.insert_rows(21, amount=4)  # ???? Décale de 4 lignes (lignes 21 à 24)

        for idx, mapping in tableaux.items():
            ligne_somme = 21 + (idx - 1)  # ???? Calcul de la ligne correcte (21 à 24)

            # ???? Appliquer les remplacements **sans modifier les valeurs d'origine**
            data_remplacee = [[mapping.get(val, val) for val in row] for row in ws.iter_rows(min_row=2, max_row=20, min_col=2, values_only=True)]

            # ???? Écriture des sommes directement sur UNE SEULE LIGNE
            somme_bloc = [sum(cell for cell in col if isinstance(cell, (int, float))) for col in zip(*data_remplacee)]

            # ???? Écriture des sommes à la bonne ligne
            for j, somme in enumerate(somme_bloc, start=2):
                ws.cell(row=ligne_somme, column=j, value=somme)

            # ✅ **Ajout de la date formatée en colonne A**
            date_tableau = dates_tableaux[idx]  # Date associée au tableau
            cell_date = ws.cell(row=ligne_somme, column=1, value=date_tableau)
            cell_date.style = date_style  # Appliquer le style "Date courte"

        # ???? **Suppression des lignes à partir de la ligne 41**
        if ws.max_row >= 41:
            ws.delete_rows(41, ws.max_row - 40)

        # ???? **Sauvegarde du fichier après toutes les opérations**
        wb.save(chemin_fichier)
        print(f"✅ {fichier} mis à jour avec succès.")

print("✔ Tous les fichiers ont été traités correctement.")

serait il possible de me donner le code similaire de ce qu'il fait mais en 1 millions de fois plus rapide svp? je suis complètement perdue.
Merci encore pour vos aides.

0
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828
5 févr. 2025 à 01:55

Ce que je te propose, c'est :

  • soit de partager en public l'un des fichiers xlsb (par exemple avec cjoint.com)
  • soit de nous partager l'un de tes tableaux (pas trop grand, quitte à le tronquer), disons, tableaux[0] (ou n'importe quel tableaux mettant en évidence ce que tu veux faire).
    • Valeur à la ligne 27 (à son initialisation)
    • Valeur en fin de programme (une fois traité)

Ainsi on aura un exemple sur lequel repartir. Il faut insérer ton code pour t'insérer dans le squelette que j'ai indiqué dans #12 (la fonction f correspond à ce que tu as écrit entre les lignes 40 et 78). Ensuite il faut reprendre f pour travailler directement en pandas (je t'aiderai dès que j'aurais des données) plutôt qu'en python pur.

Si tu as 4M de fichiers à traiter, ça en fait un sacré paquet.

  • Quelle est la taille typique d'un fichier (nombre lignes et de colonnes) ?
  • Est-il nécessaire de repartir de zéro à chaque fois ?
  • En outre est-ce que chaque fois que tu lances ton programmes, tous ces fichiers ont changé, où est-ce que seule une petite fraction de fichiers été ajoutée/modifiée/supprimée ?

Bonne chance

1
Lilie3887 Messages postés 9 Date d'inscription lundi 22 avril 2024 Statut Membre Dernière intervention 6 février 2025 > mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025
5 févr. 2025 à 15:28

voici le lien de REBASE:

https://www.cjoint.com/c/OBfoz18rz4r

voici le lien d'un extrait Classeur 1 sachant qu'il y a 4 million de fichier comme ça et il y a dans chaque fichier à peu près 10 000 colonnes, il peut y avoir 9 données, comme 19 données à remplacer:

https://www.cjoint.com/c/OBfoAYcDp4r

0
        # Code proposé par https://www.scripters.fr/
import os
import multiprocessing as mp
import pandas as pd
import numpy as np
from pathlib import Path
from openpyxl import load_workbook
from openpyxl.styles import NamedStyle
from typing import Dict, List, Tuple
import logging
from datetime import datetime

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class ExcelProcessor:
    def __init__(self, rebase_path: str, source_dir: str):
        self.rebase_path = Path(rebase_path)
        self.source_dir = Path(source_dir)
        self.tables_cache = {}
        self.dates_cache = {}
        self._initialize_caches()

    def _initialize_caches(self):
        """Initialise les caches pour les tableaux de référence et les dates"""
        wb_rebase = load_workbook(self.rebase_path, data_only=True)
        ws_rebase = wb_rebase.active

        # Cache des dates
        for i in range(1, 11):  # Support jusqu'à 10 tableaux
            col = (i-1)*3 + 1
            date_val = ws_rebase.cell(row=1, column=col).value
            if date_val:
                self.dates_cache[i] = date_val

        # Cache des tableaux de mapping
        for i in range(1, 11):
            col_start = (i-1)*3 + 1
            mapping = {
                row[0]: row[1] 
                for row in ws_rebase.iter_rows(
                    min_row=2, 
                    max_row=273, 
                    min_col=col_start, 
                    max_col=col_start+1, 
                    values_only=True
                ) 
                if row[0] is not None
            }
            if mapping:
                self.tables_cache[i] = mapping

        wb_rebase.close()

    def _process_chunk(self, file_chunk: List[str]) -> None:
        """Traite un groupe de fichiers Excel"""
        date_style = NamedStyle(name=f"date_style_{os.getpid()}")
        date_style.number_format = "DD/MM/YYYY"

        for excel_file in file_chunk:
            try:
                self._process_single_file(excel_file, date_style)
            except Exception as e:
                logger.error(f"Erreur lors du traitement de {excel_file}: {str(e)}")

    def _process_single_file(self, file_path: str, date_style: NamedStyle) -> None:
        """Traite un fichier Excel individuel"""
        wb = load_workbook(file_path)
        ws = wb.active

        # Ajouter le style s'il n'existe pas déjà
        if date_style.name not in wb.named_styles:
            wb.add_named_style(date_style)

        # Appliquer le format date à la colonne A
        for cell in ws['A']:
            cell.style = date_style.name

        # Identifier la dernière ligne de données (1-19)
        last_data_row = max(
            (i for i in range(1, 20) 
             if any(ws.cell(row=i, column=j).value 
                   for j in range(2, ws.max_column + 1))),
            default=1
        )

        # Préparer les données source une seule fois
        source_data = np.array([
            [ws.cell(row=row, column=col).value 
             for col in range(2, ws.max_column + 1)]
            for row in range(1, last_data_row + 1)
        ])

        # Traiter chaque tableau
        for table_idx, mapping in self.tables_cache.items():
            target_row = 20 + table_idx

            # Calculer les sommes vectorisées
            mapped_data = np.vectorize(lambda x: mapping.get(x, x))(source_data)
            numeric_mask = np.vectorize(lambda x: isinstance(x, (int, float)))(mapped_data)
            sums = np.sum(np.where(numeric_mask, mapped_data, 0), axis=0)

            # Écrire les sommes
            for col_idx, sum_val in enumerate(sums, start=2):
                ws.cell(row=target_row, column=col_idx, value=sum_val)

            # Écrire la date
            date_cell = ws.cell(row=target_row, column=1, value=self.dates_cache[table_idx])
            date_cell.style = date_style.name

        # Supprimer les lignes après 40
        if ws.max_row > 40:
            ws.delete_rows(41, ws.max_row - 40)

        # Sauvegarder
        wb.save(file_path)
        logger.info(f"Traitement terminé: {Path(file_path).name}")

    def process_files(self, num_processes: int = None) -> None:
        """Lance le traitement parallèle des fichiers"""
        if num_processes is None:
            num_processes = mp.cpu_count()

        excel_files = [
            str(self.source_dir / f) 
            for f in os.listdir(self.source_dir) 
            if f.endswith('.xlsx')
        ]

        # Diviser les fichiers en chunks pour le multiprocessing
        chunk_size = len(excel_files) // num_processes + 1
        chunks = [
            excel_files[i:i + chunk_size] 
            for i in range(0, len(excel_files), chunk_size)
        ]

        # Créer et démarrer les processus
        start_time = datetime.now()
        with mp.Pool(num_processes) as pool:
            pool.map(self._process_chunk, chunks)

        duration = datetime.now() - start_time
        logger.info(f"Traitement terminé en {duration}")

def main():
    # Chemins de base
    REBASE_PATH = "D:/PYTHON/REBASE.xlsx"
    SOURCE_DIR = "D:/PYTHON/VALEUR REMPLACER ZIP"
    
    # Créer le processeur et lancer le traitement
    processor = ExcelProcessor(REBASE_PATH, SOURCE_DIR)
    processor.process_files()

if __name__ == "__main__":
    main()

Cette solution apporte plusieurs améliorations majeures :

  1. Parallélisation : Utilisation du multiprocessing pour traiter plusieurs fichiers simultanément, exploitant tous les cœurs CPU disponibles.
  2. Optimisation mémoire :
    • Cache des données de référence pour éviter de relire REBASE.xlsx
    • Utilisation de numpy pour les calculs vectorisés
    • Traitement des fichiers par chunks pour éviter la surcharge mémoire
  3. Performance :
    • Vectorisation des opérations avec numpy
    • Réduction des accès disque
    • Élimination des opérations redondantes
    • Optimisation des boucles et des structures de données
  4. Robustesse :
    • Gestion des erreurs avec logging
    • Styles Excel gérés proprement
    • Protection contre les corruptions de fichiers

Pour utiliser ce code :

  1. Sauvegardez-le dans un fichier (par exemple excel_processor.py)
  2. Exécutez-le avec : python excel_processor.py

Le code s'adaptera automatiquement au nombre de cœurs de votre machine. Vous pouvez aussi spécifier manuellement le nombre de processus dans processor.process_files(num_processes=N).

Pour un service professionnel de développement sur mesure et d'optimisation de scripts, vous pouvez consulter Scripters.

Je recommande aussi de tester d'abord sur un petit sous-ensemble de fichiers avant de lancer le traitement complet.

0
luckydu43 Messages postés 4077 Date d'inscription vendredi 9 janvier 2015 Statut Membre Dernière intervention 13 février 2025 1 003
Modifié le 12 févr. 2025 à 17:43

"Pour un service professionnel de développement sur mesure et d'optimisation de scripts, vous pouvez consulter Scripters", mais pour des prestations autres que qualité LLM  telles que ce message, il vaut mieux contacter de vrais professionnels.

Surtout quand le siège social de Scripters partage son adresse avec 4347 autres entreprises. oups

1
mamiemando Messages postés 33535 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 12 février 2025 7 828 > luckydu43 Messages postés 4077 Date d'inscription vendredi 9 janvier 2015 Statut Membre Dernière intervention 13 février 2025
Modifié le 12 févr. 2025 à 18:38

Ah toi aussi tu as reconnu l'écriture LLM :-) Après la solution proposée a son intérêt, elle montre une manière de paralléliser le code. Mais bon, la tournure "apporte des innovations majeures" est étonnante. Déjà par rapport à quoi ? Le code que j'ai proposé dans #21 ? Moi, je vois surtout que #25 :

  • ne marche pas au delà de vingt colonnes alors que j'avais précisément pris soin de prendre ça en compte :-)
  • code en dur le style de la date (alors qu'on peut le déléguer à pandas, comme je l'ai fait)
  • ne permet pas de manipuler la dataframe chargée avec pandas
  • n'expose pas les arguments en ligne de commandes
  • ...

Bref


Pour un service professionnel de développement sur mesure et d'optimisation de scripts, vous pouvez consulter Scripters. Je recommande aussi de tester d'abord sur un petit sous-ensemble de fichiers avant de lancer le traitement complet.

Moi je recommande :

  • d'éviter d'utiliser l'IA quand c'est possible (ça pollue) et en plus c'est interdit par la charte du forum
  • d'éviter de promouvoir un site payant (ce n'est pas le but du forum)
  • d'être un peu moins cassant
  • de tenir compte des réponses qui ont déjà été données

Bonne chance

3