使用 MultiIndex 时如何在 Pandas 中使用转换器

How to use converter in Pandas when using a MultiIndex

问题

我有一个 excel table,其中第一行是 header,第二行是该列其余部分的测量单位(即纳米、微米) . Pandas 提供了一个 excellent read_excel 函数,我可以在其中传递一个转换器字典。字典的键是列名,值是一个 lambda 函数,它将 excel 值转换为我想要的其他值。在这种情况下,我使用的任何指标的基值(纳米到米)。

我似乎无法弄清楚如何让我的转换器字典使用第二个 header 行(度量单位行)。如果我只指定我的 headers 来获取单位行,但我希望实际标签包含在我的 header.

这是我的代码

import numpy as np
import pandas as pd
import re
import os
from typing import Dict
from pandas.core.frame import DataFrame

Converters = {
  "GPa": lambda gpa: gpa * 1_000_000_000,
  "nm": lambda nm: nm / 1_000_000_000,
  "microns": lambda microns: microns / 1_000_000 
}

# Read and load metadata
directory = data_directory + "/" + metadata_directory
filenames = sorted(os.listdir(directory))
for filename in filenames:
  readData = pd.read_excel("./" + directory + "/" + filename, header=[0,1], converters=Converters)
  print(filename, "\n", readData.head(2))

OS 规格

设备名称DESKTOP-AE4IMFH 处理器 Intel(R) Core(TM) i7-1065G7 CPU @ 1.30GHz 1.50 GHz 安装 RAM 12.0 GB(可用 11.8 GB) 设备编号 2B55F49B-6877-455D-88C5-D369A23FB40C 产品编号 00325-96685-10579-AAOEM 系统类型 64 位操作系统,基于 x64 的处理器 笔和触摸笔和触摸支持 10 个触摸点

版本Windows10家 版本 20H2 安装于 2020 年 7 月 23 日 OS 内部版本 19042.1052 体验Windows功能体验包120.2212.2020.0

Python 版本 3.9.5

我试过的

摆脱 MultiIndex 并仅将 header 指定为第 1 行效果很好。但是,我真的希望将列名作为 header.

的一部分

一个想法可能是将 DataFrame 转换为 numpy 数组,然后找到与每个 Converter 函数名称匹配的列索引。然后我们可以手动将转换应用于该列索引处的每一行。然而,这感觉很老套,很想找到一个更干净的解决方案

我不确定我是否完全理解您的意图。尽管如此, 这是一个建议:

在下文中,我使用 Excel-文件 test.xlsx 作为示例,内容为

col_1  col_2  col_3
    1      2      3
    1      1      1
    2      2      2
    3      3      3

这个

from operator import mul
from functools import partial

units = pd.read_excel('test.xlsx', nrows=1)
converters = {
    col: partial(mul, 1 / units.at[0, col])
    for col in units.columns
}
df = pd.read_excel('test.xlsx', skiprows=[1], converters=converters)

产生以下数据帧df

   col_1  col_2     col_3
0    1.0    0.5  0.333333
1    2.0    1.0  0.666667
2    3.0    1.5  1.000000

此处不包括包含单位的行。如果要包含它,请将最后一行替换为:

df = pd.concat([
         units,
         pd.read_excel('test.xlsx', skiprows=[1], converters=converters)
     ]).reset_index(drop=True)

结果:

   col_1  col_2     col_3
0    1.0    2.0  3.000000
1    1.0    0.5  0.333333
2    2.0    1.0  0.666667
3    3.0    1.5  1.000000

(如果您想知道为什么我没有使用 lambda 来定义转换器:如果您通过变量定义它们,这通常会失败。)

所以,如果您想将其集成到您的代码中,它看起来像:

from operator import mul
from functools import partial

...

for filename in filenames:
    filepath = "./" + directory + "/" + filename
    units = pd.read_excel(filepath, nrows=1)
    converters = {
       col: partial(mul, 1 / units.at[0, col])
       for col in units.columns
    }
   readData = pd.read_excel(filepath, skiprows=[1], converters=converters)

编辑:今天重新思考这个问题后,我意识到使用转换器可能不是最好的方法。转换器功能非常基础(简单除法),因此有更好的解决方案:

for filename in filenames:
   readData = pd.read_excel("./" + directory + "/" + filename)

   # Version 1: Discarding row with units
   readData = (readData.iloc[1:, :] / readData.iloc[0, :]).reset_index(drop=True)
   # Version 2: Keeping row with units
   readData.iloc[1:, :] /= readData.iloc[0, :]