有没有办法忽略大小写?
我有一个数据记录仪,它在数据集中创建的列名混合大小写,例如 "SampleTime" 和 "SensorData"。
经固件更新后,列名仍然相同,但仅以大写字母出现,例如 "SAMPLETIME" 和 "SENSORDATA"。
为了快速分析这些数据,我使用一个小型的Python脚本。在这个脚本中,我将列名用于各种计算和图表。脚本很容易进行适配。
不过,我变得好奇,想在Python里学点新东西。除了在脚本中硬编码名称并不明智这一点(这是另一个问题,与我的问题无关):有没有办法在不事先检查数据集是使用旧固件还是新固件、也不事先把列名全部转换为全大写或全小写的情况下,使用同一个脚本来处理这两种数据集?(使用 upper() 或 lower(),或在没有事先 rename() 的情况下。)也就是说,是否有办法忽略大小写敏感性?
举个例子,我可以给出一个小脚本来说明我的需求:
import pandas as pd
df = pd.DataFrame([['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]]
columns=['SampleTime', 'UTCs', 'SensorData1', 'SensorData2'])
print(df)
这展示了一个基于旧固件的数据集:
SampleTime UTCs SensorData1 SensorData2
0 2024-06-21 06:22:38 22958 605.968389 0.994548
1 2024-06-21 06:22:39 22959 616.009398 0.983443
2 2024-06-21 06:22:40 22960 624.630573 0.973647
3 2024-06-21 06:22:41 22961 633.476367 1.017651
4 2024-06-21 06:22:42 22962 642.322161 5.017651
如前所述,还有通过如下编码进行的进一步计算/绘图:
df['result'] = (df.SensorData1 - df.SensorData2) / (df.SensorData1 + df.SensorData2)
我也想知道是否有办法同样使用一个采用新固件的数据集:
import pandas as pd
df = pd.DataFrame([['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]]
columns=['SAMPLETIME', 'UTCS', 'SENSORDATA1', 'SENSORDATA2'])
print(df)
这展示了一个基于新固件的数据集:
SAMPLETIME UTCS SENSORDATA1 SENSORDATA2
0 2024-06-21 06:22:38 22958 605.968389 0.994548
1 2024-06-21 06:22:39 22959 616.009398 0.983443
2 2024-06-21 06:22:40 22960 624.630573 0.973647
3 2024-06-21 06:22:41 22961 633.476367 1.017651
4 2024-06-21 06:22:42 22962 642.322161 5.017651
像使用upper() 和lower() 那样,我只能得到全大写或全小写。因此,旧脚本在计算中也使用了大小写混合的写法。
到底有没有办法?
解决方案
动机:
我有一堆代码在寻找带有特定大小写字符串的列名,而我的旧数据使用这些名字,新数据也使用这些名字但大小写不同。
问题:
如何在不更新所有代码的情况下,确保名称兼容?
答案:
当然需要更新列名,为此我们将使用一个旧数据集,其列名代表你的规范名称,以及任意一个你想要确保具备规范名称的数据集作为示例。我们将通过一个小的查找函数来处理翻译。如果你事先知道规范名称,可以直接使用一个名称列表,而不必加载一个“旧”数据集来通过df_old.columns获取列表。
请注意,这样的方法对旧数据集和新数据集都安全,能确保得到正确的名称;如果新的数据集有一个不在规范列表中的列名,也会原样通过。
def get_canonical_names(noncannon, canon):
canon = {name.lower(): name for name in canon}
return [canon.get(name.lower(), name) for name in noncannon]
通过这样的方式,我们现在可以做到:
import pandas as pd
def get_canonical_names(noncannon, canon):
canon = {name.lower(): name for name in canon}
return [canon.get(name.lower(), name) for name in noncannon]
df_old = pd.DataFrame(
[
['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]
],
columns=['SampleTime', 'UTCs', 'SensorData1', 'SensorData2']
)
df_new = pd.DataFrame(
[
['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]
],
columns=['SAMPLETIME', 'UTCS', 'SENSORDATA1', 'SENSORDATA2']
)
# safe to run on older and newer datasets
df_new.columns = get_canonical_names(df_new.columns, df_old.columns)
df_old.columns = get_canonical_names(df_old.columns, df_old.columns)
print(df_old)
print(df_new)
并得到:
SampleTime UTCs SensorData1 SensorData2
0 2024-06-21 06:22:38 22958 605.968389 0.994548
1 2024-06-21 06:22:39 22959 616.009398 0.983443
2 2024-06-21 06:22:40 22960 624.630573 0.973647
3 2024-06-21 06:22:41 22961 633.476367 1.017651
4 2024-06-21 06:22:42 22962 642.322161 5.017651
SampleTime UTCs SensorData1 SensorData2
0 2024-06-21 06:22:38 22958 605.968389 0.994548
1 2024-06-21 06:22:39 22959 616.009398 0.983443
2 2024-06-21 06:22:40 22960 624.630573 0.973647
3 2024-06-21 06:22:41 22961 633.476367 1.017651
4 2024-06-21 06:22:42 22962 642.322161 5.017651
补充说明:
如果这是我经常要做的事情,我大概会考虑用闭包来定义我的方法,这样就不需要一直引用规范名称的列表。也许可以这样:
def get_canonical_names_builder(canon):
canon = {name.lower(): name for name in canon}
return lambda noncannon: [canon.get(name.lower(), name) for name in noncannon]
这段代码把我们的规范名称列表转换成一个会接受名称列表并进行转换的方法。返回的方法的工作方式就像旧的方法,但现在只需要一个参数。
用法示例:
import pandas as pd
def get_canonical_names_builder(canon):
canon = {name.lower(): name for name in canon}
return lambda noncannon: [canon.get(name.lower(), name) for name in noncannon]
df_old = pd.DataFrame(
[
['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]
],
columns=['SampleTime', 'UTCs', 'SensorData1', 'SensorData2']
)
df_new = pd.DataFrame(
[
['2024-06-21 06:22:38', 22958 ,605.968389, 0.994548],
['2024-06-21 06:22:39', 22959 ,616.009398, 0.983443],
['2024-06-21 06:22:40', 22960 ,624.630573, 0.973647],
['2024-06-21 06:22:41', 22961 ,633.476367, 1.017651],
['2024-06-21 06:22:42', 22962 ,642.322161, 5.017651]
],
columns=['SAMPLETIME', 'UTCS', 'SENSORDATA1', 'SENSORDATA2']
)
get_canonical_names = get_canonical_names_builder(df_old.columns)
df_new.columns = get_canonical_names(df_new.columns)
df_old.columns = get_canonical_names(df_old.columns)
print(df_old)
print(df_new)