Skip to content

Pandas Melt : remodeler des données larges en format long (guide complet)

Updated on

Vous avez une feuille de calcul où chaque mois est une colonne séparée — January, February, March, jusqu’à December. Douze colonnes de chiffres de revenus, et vous devez tracer une série temporelle, faire un groupby ou alimenter un modèle de machine learning. Aucune de ces opérations ne fonctionne bien avec des données au format wide. Il vous faut une colonne pour le nom du mois et une colonne pour la valeur de revenu. Cette transformation — transformer des colonnes en lignes — est exactement ce que fait pandas melt.

Le problème s’aggrave vite. Les jeux de données wide avec des dizaines ou des centaines de colonnes sont courants dans les données d’enquête, les relevés de capteurs, les rapports financiers et les exports pivotés depuis Excel ou SQL. Les restructurer manuellement est fastidieux et propice aux erreurs. Chaque colonne supplémentaire à dé-pivoter implique davantage de code répétitif si vous essayez de le faire sans l’outil adapté.

La fonction pd.melt() résout cela en un seul appel. Elle prend un DataFrame au format wide et le « melt » en format long en transformant certaines colonnes en lignes, tout en conservant intactes vos colonnes d’identifiant. Ce guide couvre la syntaxe complète, chaque paramètre, des exemples pratiques issus de cas réels, ainsi que des comparaisons avec des fonctions de remodelage proches comme pivot, stack et wide_to_long.

📚

Ce que fait pd.melt()

pd.melt() dé-pivote un DataFrame du format wide vers le format long. En format wide, chaque variable a sa propre colonne. En format long (aussi appelé format « tidy »), il y a une colonne pour les noms de variables et une colonne pour les valeurs.

Voici la transformation conceptuelle :

Format wide (avant melt) :

studentmathscienceenglish
Alice908588
Bob789280

Format long (après melt) :

studentsubjectscore
Alicemath90
Alicescience85
Aliceenglish88
Bobmath78
Bobscience92
Bobenglish80

La colonne student est conservée comme identifiant, tandis que les trois colonnes de matières sont « melt » en deux colonnes : l’une contenant le nom de la matière, et l’autre contenant la note.

Syntaxe et paramètres de pd.melt()

pd.melt(frame, id_vars=None, value_vars=None, var_name=None,
        value_name='value', col_level=None, ignore_index=True)

Vous pouvez aussi l’appeler comme une méthode directement sur le DataFrame :

df.melt(id_vars=None, value_vars=None, var_name=None,
        value_name='value', col_level=None, ignore_index=True)

Référence des paramètres

ParamètreDescriptionValeur par défaut
frameLe DataFrame à « melt » (inutile si vous utilisez df.melt())Requis
id_varsColonne(s) à conserver comme variables d’identification (non « melt »)None
value_varsColonne(s) à dé-pivoter en lignes. Si omis, toutes les colonnes non dans id_vars sont utiliséesNone
var_nameNom de la nouvelle colonne contenant les noms de colonnes d’origine'variable'
value_nameNom de la nouvelle colonne contenant les valeurs'value'
col_levelSi les colonnes sont un MultiIndex, le niveau à « melt »None
ignore_indexSi True, le résultat utilise un nouvel index entier. Si False, l’index d’origine est conservéTrue

Exemple de base : notes d’élèves

Repartons de l’exemple des notes d’élèves ci-dessus :

import pandas as pd
 
grades = pd.DataFrame({
    'student': ['Alice', 'Bob', 'Charlie'],
    'math': [90, 78, 85],
    'science': [85, 92, 88],
    'english': [88, 80, 91]
})
 
long = pd.melt(grades, id_vars=['student'], value_vars=['math', 'science', 'english'],
               var_name='subject', value_name='score')
print(long)

Output:

   student  subject  score
0    Alice     math     90
1      Bob     math     78
2  Charlie     math     85
3    Alice  science     85
4      Bob  science     92
5  Charlie  science     88
6    Alice  english     88
7      Bob  english     80
8  Charlie  english     91

Chaque ligne représente maintenant une combinaison élève-matière. Le DataFrame d’origine en 3x4 (3 lignes, 4 colonnes) devient un DataFrame 9x3 (9 lignes, 3 colonnes).

Omettre value_vars

Si vous ne renseignez pas value_vars, pandas « melt » chaque colonne qui n’est pas listée dans id_vars :

long = grades.melt(id_vars=['student'], var_name='subject', value_name='score')
print(long)

Cela produit le même résultat que l’exemple précédent. Omettre value_vars est pratique quand vous voulez « melt » toutes les colonnes non-identifiantes.

Melt sans id_vars

Vous pouvez aussi effectuer un melt sans spécifier de colonnes d’identifiant. Cela met toutes les colonnes dans la paire variable/valeur :

temperatures = pd.DataFrame({
    'Jan': [30, 28],
    'Feb': [32, 31],
    'Mar': [45, 42]
})
 
long = temperatures.melt(var_name='month', value_name='temp_f')
print(long)

Output:

  month  temp_f
0   Jan      30
1   Jan      28
2   Feb      32
3   Feb      31
4   Mar      45
5   Mar      42

C’est utile lorsque chaque colonne est une mesure et qu’il n’y a aucun identifiant à conserver.

Plusieurs colonnes d’identifiant

Les jeux de données réels ont souvent plus d’un identifiant. Vous pouvez passer une liste de noms de colonnes à id_vars :

sales = pd.DataFrame({
    'region': ['North', 'South', 'North', 'South'],
    'product': ['Widget', 'Widget', 'Gadget', 'Gadget'],
    'q1_revenue': [1200, 1500, 800, 950],
    'q2_revenue': [1400, 1600, 900, 1100],
    'q3_revenue': [1100, 1450, 850, 1000],
    'q4_revenue': [1500, 1700, 1000, 1200]
})
 
long_sales = sales.melt(
    id_vars=['region', 'product'],
    var_name='quarter',
    value_name='revenue'
)
print(long_sales)

Output:

    region product      quarter  revenue
0    North  Widget   q1_revenue     1200
1    South  Widget   q1_revenue     1500
2    North  Gadget   q1_revenue      800
3    South  Gadget   q1_revenue      950
4    North  Widget   q2_revenue     1400
5    South  Widget   q2_revenue     1600
6    North  Gadget   q2_revenue      900
7    South  Gadget   q2_revenue     1100
8    North  Widget   q3_revenue     1100
9    South  Widget   q3_revenue     1450
10   North  Gadget   q3_revenue      850
11   South  Gadget   q3_revenue     1000
12   North  Widget   q4_revenue     1500
13   South  Widget   q4_revenue     1700
14   North  Gadget   q4_revenue     1000
15   South  Gadget   q4_revenue     1200

region et product sont tous deux conservés pour chaque ligne. Les quatre colonnes trimestrielles sont condensées en deux colonnes.

Nettoyer après un melt

Après un melt, la colonne variable contient souvent des chaînes que vous voulez nettoyer. Dans l’exemple ci-dessus, la colonne du trimestre a des valeurs comme q1_revenue au lieu de simplement Q1. Utilisez des opérations de chaîne pour corriger cela :

long_sales['quarter'] = long_sales['quarter'].str.replace('_revenue', '').str.upper()
print(long_sales.head())

Output:

  region product quarter  revenue
0  North  Widget      Q1     1200
1  South  Widget      Q1     1500
2  North  Gadget      Q1      800
3  South  Gadget      Q1      950
4  North  Widget      Q2     1400

Melt de colonnes sélectionnées uniquement

Parfois, vous ne voulez « melt » qu’un sous-ensemble de colonnes. Spécifiez-les explicitement dans value_vars :

survey = pd.DataFrame({
    'respondent': ['R1', 'R2', 'R3'],
    'age': [25, 34, 42],
    'q1_satisfaction': [4, 5, 3],
    'q2_satisfaction': [3, 4, 5],
    'q3_satisfaction': [5, 3, 4],
    'income': [50000, 75000, 60000]
})
 
# Only melt the satisfaction columns, keep age and income as identifiers
long_survey = survey.melt(
    id_vars=['respondent', 'age', 'income'],
    value_vars=['q1_satisfaction', 'q2_satisfaction', 'q3_satisfaction'],
    var_name='question',
    value_name='rating'
)
print(long_survey)

Output:

  respondent  age  income         question  rating
0         R1   25   50000  q1_satisfaction       4
1         R2   34   75000  q1_satisfaction       5
2         R3   42   60000  q1_satisfaction       3
3         R1   25   50000  q2_satisfaction       3
4         R2   34   75000  q2_satisfaction       4
5         R3   42   60000  q2_satisfaction       5
6         R1   25   50000  q3_satisfaction       5
7         R2   34   75000  q3_satisfaction       3
8         R3   42   60000  q3_satisfaction       4

La colonne income est conservée comme identifiant même si elle ne fait pas partie des colonnes « melt ».

Exemple concret : données de séries temporelles

Les données financières et économiques arrivent souvent en format wide avec des dates comme en-têtes de colonnes. Un melt convertit cela en une série temporelle facile à tracer :

import pandas as pd
 
gdp = pd.DataFrame({
    'country': ['USA', 'UK', 'Germany'],
    '2020': [20.94, 2.71, 3.89],
    '2021': [23.00, 3.12, 4.26],
    '2022': [25.46, 3.07, 4.07],
    '2023': [27.36, 3.33, 4.46]
})
 
gdp_long = gdp.melt(id_vars=['country'], var_name='year', value_name='gdp_trillion_usd')
gdp_long['year'] = gdp_long['year'].astype(int)
gdp_long = gdp_long.sort_values(['country', 'year']).reset_index(drop=True)
print(gdp_long)

Output:

   country  year  gdp_trillion_usd
0  Germany  2020              3.89
1  Germany  2021              4.26
2  Germany  2022              4.07
3  Germany  2023              4.46
4       UK  2020              2.71
5       UK  2021              3.12
6       UK  2022              3.07
7       UK  2023              3.33
8      USA  2020             20.94
9      USA  2021             23.00
10     USA  2022             25.46
11     USA  2023             27.36

Vous pouvez maintenant facilement tracer le PIB dans le temps, regrouper par pays, ou calculer la croissance d’une année sur l’autre.

Melt avec des colonnes MultiIndex

Si votre DataFrame a des en-têtes de colonnes multi-niveaux, utilisez le paramètre col_level pour préciser quel niveau « melt » :

arrays = [['score', 'score', 'attendance', 'attendance'],
          ['midterm', 'final', 'midterm', 'final']]
columns = pd.MultiIndex.from_arrays(arrays, names=['metric', 'exam'])
 
data = pd.DataFrame([[85, 90, 95, 100], [78, 82, 90, 88]],
                     index=['Alice', 'Bob'], columns=columns)
 
# Melt the top level
melted = data.melt(col_level=0, var_name='metric', value_name='value', ignore_index=False)
print(melted)

Pour des scénarios multi-niveaux complexes, vous devrez peut-être d’abord aplatir les colonnes avec droplevel() ou en joignant les niveaux avec un underscore avant de faire le melt.

Melt vs Pivot : opérations inverses

melt() et pivot() sont des opérations inverses. Melt convertit wide → long. Pivot convertit long → wide.

import pandas as pd
 
# Start wide
wide = pd.DataFrame({
    'name': ['Alice', 'Bob'],
    'math': [90, 78],
    'science': [85, 92]
})
 
# Melt: wide -> long
long = wide.melt(id_vars='name', var_name='subject', value_name='score')
print("Long format:")
print(long)
 
# Pivot: long -> wide (round-trip)
back_to_wide = long.pivot(index='name', columns='subject', values='score').reset_index()
back_to_wide.columns.name = None
print("\nBack to wide format:")
print(back_to_wide)

Output:

Long format:
    name  subject  score
0  Alice     math     90
1    Bob     math     78
2  Alice  science     85
3    Bob  science     92

Back to wide format:
    name  math  science
0  Alice    90       85
1    Bob    78       92

Différence clé : pivot() exige des combinaisons index-colonne uniques. Si vos données longues contiennent des doublons, utilisez plutôt pivot_table() avec une fonction d’agrégation.

Melt vs Stack vs wide_to_long

Pandas fournit plusieurs fonctions de remodelage. Voici quand utiliser chacune :

FonctionDirectionIdéal pourDifférence clé
melt()wide → longDé-pivoter des colonnes spécifiques en lignesBasé sur les colonnes ; le plus intuitif pour débuter
stack()wide → longTransformer des niveaux de colonnes en niveaux d’indexBasé sur l’index ; fonctionne avec MultiIndex
wide_to_long()wide → longColonnes avec préfixe commun et suffixe numérique (ex. score1, score2)Analyse automatiquement les « stub names »
pivot()long → wideÉtaler des valeurs en colonnes (clés uniques)Inverse de melt()
unstack()long → wideTransformer des niveaux d’index en colonnesInverse de stack()

Quand utiliser stack() à la place

stack() opère sur l’index des colonnes et le pousse dans l’index des lignes. C’est surtout utile si vous travaillez déjà avec un MultiIndex et que vous voulez remodeler au niveau de l’index :

wide = pd.DataFrame({
    'math': [90, 78],
    'science': [85, 92]
}, index=['Alice', 'Bob'])
 
stacked = wide.stack()
print(stacked)

Output:

Alice  math       90
       science    85
Bob    math       78
       science    92
dtype: int64

Le résultat est une Series avec un MultiIndex, pas un DataFrame avec des colonnes nommées comme produit melt(). Utilisez melt() lorsque vous voulez un DataFrame propre avec des colonnes explicitement nommées.

Quand utiliser wide_to_long()

wide_to_long() est conçu pour des colonnes qui suivent un schéma de nommage comme score1, score2, score3 :

df = pd.DataFrame({
    'student': ['Alice', 'Bob'],
    'score1': [90, 78],
    'score2': [85, 92],
    'score3': [88, 80]
})
 
long = pd.wide_to_long(df, stubnames='score', i='student', j='exam_num')
print(long.reset_index())

Output:

  student  exam_num  score
0   Alice         1     90
1   Alice         2     85
2   Alice         3     88
3     Bob         1     78
4     Bob         2     92
5     Bob         3     80

Utilisez wide_to_long() lorsque vos colonnes ont un motif préfixe-suffixe cohérent. Sinon, melt() est plus flexible.

Considérations de performance

Pour la plupart des jeux de données (moins de quelques millions de lignes), melt() est suffisamment rapide. Voici des benchmarks sur un DataFrame de 100 000 lignes et 50 colonnes « melt » :

OpérationTemps approximatif
melt() avec 50 colonnes de valeurs~15 ms
Équivalent stack()~10 ms
Boucle manuelle avec concat()~500 ms

Astuces pour de meilleures performances :

  1. Spécifiez value_vars explicitement — « melt » uniquement les colonnes nécessaires est plus rapide que tout « melt ».
  2. Utilisez ignore_index=True (valeur par défaut) — conserver l’index d’origine ajoute du surcoût.
  3. Évitez de melt puis re-pivoter immédiatement — si vous avez besoin d’un autre format wide, envisagez pivot_table() ou rename() directement plutôt qu’un aller-retour.
  4. Pour des DataFrames très volumineux (100M+ de lignes après melt), envisagez d’utiliser polars ou Dask, qui offrent l’évaluation lazy et le traitement parallèle.
# Only melt the columns you actually need
long = df.melt(
    id_vars=['id'],
    value_vars=['col_a', 'col_b', 'col_c'],  # Not all 50 columns
    var_name='metric',
    value_name='reading'
)

Erreurs fréquentes et comment les corriger

1. KeyError : colonne introuvable

Cela arrive lorsqu’un nom de colonne dans id_vars ou value_vars n’existe pas dans le DataFrame :

# Wrong: column name has a typo
long = df.melt(id_vars=['stduent'])  # KeyError
 
# Fix: check column names first
print(df.columns.tolist())

2. Doublons inattendus

Melt ne crée pas lui-même des doublons — il crée une ligne par combinaison variables d’identification/valeur. Si vous voyez des doublons, cela signifie que vos données d’origine avaient des lignes d’identifiant dupliquées :

# Check for duplicates in your id columns
print(df.duplicated(subset=['student']).sum())

3. Types de données mixtes dans la colonne de valeur

Lorsque vous « melt » des colonnes de types différents (par ex. int64 et float64), pandas convertit la colonne de valeurs vers le type le plus général. Si vous « melt » un mélange de colonnes numériques et de chaînes, la colonne de valeurs devient de type object :

df = pd.DataFrame({
    'id': [1, 2],
    'score': [90, 85],
    'grade': ['A', 'B']
})
 
long = df.melt(id_vars='id')
print(long.dtypes)
# variable    object
# value       object  <-- both score and grade become object

Pour éviter cela, « melt » les colonnes numériques et textuelles séparément.

Visualiser des données melt avec PyGWalker

Après avoir remodelé vos données du format wide vers le format long, l’étape suivante naturelle consiste à explorer le résultat visuellement — vérifier des distributions, comparer des groupes ou repérer des valeurs aberrantes. PyGWalker (opens in a new tab) est une bibliothèque Python open-source qui transforme n’importe quel pandas DataFrame en une interface d’exploration visuelle interactive de type Tableau, directement dans Jupyter Notebook.

import pandas as pd
import pygwalker as pyg
 
# Melt your wide data into long format
grades = pd.DataFrame({
    'student': ['Alice', 'Bob', 'Charlie', 'Diana'],
    'math': [90, 78, 85, 92],
    'science': [85, 92, 88, 79],
    'english': [88, 80, 91, 84]
})
 
long = grades.melt(id_vars='student', var_name='subject', value_name='score')
 
# Launch interactive visualization
walker = pyg.walk(long)

Avec PyGWalker, vous pouvez glisser subject sur l’axe des x et score sur l’axe des y, puis colorer par student pour comparer instantanément les performances par matière — sans code de graphique. Il prend en charge les bar charts, scatter plots, box plots et plus encore, via du drag-and-drop.

Essayez PyGWalker dans Google Colab (opens in a new tab), Kaggle (opens in a new tab), ou installez-le avec pip install pygwalker.

FAQ

Que fait pandas melt ?

Pandas melt() remodèle un DataFrame du format wide vers le format long. Il prend des colonnes et les transforme en lignes, en créant deux nouvelles colonnes : une pour les noms de colonnes d’origine (variable) et une pour les valeurs. On appelle aussi cela « unpivoting ».

Quelle est la différence entre melt et pivot dans pandas ?

melt() convertit le format wide en format long (les colonnes deviennent des lignes). pivot() fait l’inverse — il convertit le format long en format wide (les lignes deviennent des colonnes). Ce sont des opérations inverses. Si vous faites un melt sur un DataFrame puis un pivot du résultat avec les mêmes paramètres, vous retrouvez le DataFrame d’origine.

Quand utiliser melt plutôt que stack dans pandas ?

Utilisez melt() quand vous voulez un DataFrame propre avec des colonnes nommées et un contrôle explicite sur les colonnes à dé-pivoter. Utilisez stack() lorsque vous travaillez avec des colonnes MultiIndex et que vous voulez pousser des niveaux de colonnes dans l’index des lignes. melt() est plus intuitif pour débuter ; stack() est plus puissant pour des remodelages hiérarchiques.

Comment melt plusieurs colonnes dans pandas ?

Passez une liste de noms de colonnes au paramètre value_vars : df.melt(id_vars=['id'], value_vars=['col_a', 'col_b', 'col_c']). Toutes les colonnes listées seront dé-pivotées en lignes. Si vous omettez value_vars, pandas « melt » toutes les colonnes non listées dans id_vars.

Peut-on melt un DataFrame avec des noms de colonnes dupliqués ?

Pandas peut « melt » le DataFrame, mais le résultat peut être déroutant car la colonne variable contiendra des valeurs dupliquées. Renommez d’abord les colonnes dupliquées via df.columns = [...] ou df.rename() pour éviter toute ambiguïté.

Comment inverser une opération melt dans pandas ?

Utilisez pivot() ou pivot_table() pour reconvertir les données (long) en format wide : long.pivot(index='id', columns='variable', values='value'). Utilisez pivot_table() s’il existe des combinaisons index-colonne dupliquées nécessitant une agrégation.

Conclusion

La fonction pandas melt() est la méthode standard pour convertir des DataFrames au format wide en format long (tidy) en Python. Points clés :

  • Utilisez id_vars pour préciser quelles colonnes conserver comme identifiants.
  • Utilisez value_vars pour contrôler quelles colonnes sont « melt ». Omettez-le pour tout « melt » sauf les identifiants.
  • Utilisez var_name et value_name pour donner des noms parlants aux colonnes de sortie.
  • Melt et pivot sont inverses — utilisez melt() pour passer de wide à long et pivot() pour passer de long à wide.
  • Choisissez melt() plutôt que stack() lorsque vous voulez un DataFrame à plat avec des noms de colonnes explicites plutôt qu’une Series MultiIndex.
  • Nettoyez après le melt — utilisez des méthodes de chaîne sur la colonne variable pour retirer des préfixes/suffixes ou reformater les valeurs.

Après le remodelage, des outils comme PyGWalker (opens in a new tab) vous permettent d’explorer visuellement le résultat sans écrire de code de graphique, rendant le workflow d’analyse plus rapide et plus intuitif.

📚