使用 Python 以对数尺度插值电池容量数据

问题描述 投票:0回答:1

我正在根据

hour_rates
capacities
currents
之间的关系来插值电池容量数据。这是我的数据示例:

import numpy as np
import pandas as pd
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt

# Data from Rolls S-480 flooded battery
capacity_data = [
    [1, 135, 135], [2, 191, 95.63], [3, 221, 73.75],
    [4, 244, 60.94], [5, 263, 52.5], [6, 278, 46.25],
    [8, 300, 37.5], [10, 319, 31.88], [12, 334, 27.81],
    [15, 352, 23.45], [20, 375, 18.75], [24, 386, 16.09],
    [50, 438, 8.76], [72, 459, 6.38], [100, 486, 4.86]
]
capacity = pd.DataFrame(capacity_data, columns=['hour_rates', 'capacities', 'currents'])

各列的关系如下:

  • hour_rates (h) = capacities (Ah) / currents (A)
  • capacities (Ah) = hour_rates (h) * currents (A)
  • currents (A) = capacities (Ah) / hour_rates (h)

目标:我想使用对数缩放对

capacities
hour_rates
进行插值以获取一系列
currents
值,以提高准确性。

代码

自定义插值类和函数来实现此目的。代码如下:

from typing import Union

class interpolate1d(interp1d):
    """Extend scipy interp1d to interpolate/extrapolate per axis in log space"""
    
    def __init__(self, x, y, *args, xspace='linear', yspace='linear', **kwargs):
        self.xspace = xspace
        self.yspace = yspace
        if self.xspace == 'log': x = np.log10(x)
        if self.yspace == 'log': y = np.log10(y)
        super().__init__(x, y, *args, **kwargs)
        
    def __call__(self, x, *args, **kwargs):
        if self.xspace == 'log': x = np.log10(x)
        if self.yspace == 'log':
            return 10**super().__call__(x, *args, **kwargs)
        else:
            return super().__call__(x, *args, **kwargs)

def interpolate_cap_by_current(df: list,
                               current_values: list,
                               kind: Union[str, int] = 'linear',
                               hr_limit: int = 600
                               ):
    """
    Interpolate Battery Capacity Values From Current list values
    """
    result = 0
    if isinstance(np_data, np.ndarray):
        # Create interpolation functions for hour rates and capacities
        # Setting kind='cubic' for better fitting to nonlinear data
        hour_rate_interp_func = interpolate1d(
            df['currents'],
            df['hour_rates'],
            xspace='log',
            yspace='log',
            fill_value="extrapolate",
            kind=kind
        )
        capacity_interp_func = interpolate1d(
            df['currents'],
            df['capacities'],
            xspace='log',
            yspace='log',
            fill_value="extrapolate",
            kind=kind
        ) # , kind='cubic'

        # Calculate interpolated values for new currents
        hour_rate_interpolated = hour_rate_interp_func(current_values)
        capacity_interpolated = capacity_interp_func(current_values)

        # Create a DataFrame for the results
        real_cap = current_values * hour_rate_interpolated
        real_hr = capacity_interpolated / current_values
        result = pd.DataFrame({
            'currents': current_values,
            'hour_rates': hour_rate_interpolated,
            'capacities': capacity_interpolated,
            'calc_cap': real_cap,
            'diff_cap': capacity_interpolated - real_cap,
            'calc_hr': real_hr,
            'diff_hr': hour_rate_interpolated - real_hr,
        })
        
        result = result[result['hour_rates'] < hr_limit]
    return result

def plot_grid(major_ticks: list,
              minor_ticks: list,
              ):
    """Set X Grid ticks"""
    ax=plt.gca()
    ax.grid(True)
    ax.set_xticks(major_ticks)
    ax.set_xticks(minor_ticks, minor=True)
    ax.grid(which='minor', alpha=0.2)
    ax.grid(which='major', alpha=0.5)

可视化:

currents_list = np.array([
            0.1, 0.2, 0.4, 0.5, 0.6, 0.8, 1, 1.5, 1.7, 2, 2.2, 2.5,
            3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 17, 20, 22, 25, 27, 30, 32,
            35, 37, 40, 60, 80, 120, 150, 180, 220, 250
        ])
capacities = interpolate_cap_by_current(
    df=capacity,
    current_values=currents_list,
    kind='quadratic'
)
#  linear, nearest, nearest-up, zero, slinear, quadratic, cubic, previous, or next. zero, slinear, quadratic and cubic
plt.figure(figsize=(18, 15))
plt.subplot(3, 1, 1)
plt.plot(capacities['hour_rates'], capacities['capacities'], label='Interpolated Capacitiy')
plt.plot(capacities['hour_rates'], capacities['calc_cap'], label='Calculated Capacitiy')
plt.plot(capacity['hour_rates'], capacity['capacities'], label='Capacitiy')
plt.ylabel('Capacity (A/h)')
plt.xlabel('Hour Rate (h)')
plt.title('Battery Hour Rate / Capacity relationship')
plt.legend()
max_tick = capacities['hour_rates'].max() + 10
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)
plt.subplot(3, 1, 2)
plt.plot(capacities['hour_rates'], capacities['currents'], label='Interpolated Current (A)')
plt.plot(capacity['hour_rates'], capacity['currents'], label='Current (A)')
plt.ylabel('Current (A)')
plt.xlabel('Hour Rate (h)')
plt.title('Battery Hour Rate / Current relationship')
plt.legend()
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)
plt.subplot(3, 1, 3)
plt.plot(capacities['currents'], capacities['capacities'], label='Interpolated capacity / current')
plt.plot(capacities['currents'], capacities['calc_cap'], label='Calculated capacity / current')
plt.plot(capacity['currents'], capacity['capacities'], label='capacity / current')
plt.ylabel('Capacity (A/h)')
plt.xlabel('Current (A)')
plt.title('Battery Current / Capacity relationship')
plt.xscale('linear')
plt.yscale('linear')
plt.legend()
max_tick = capacities['currents'].max() + 10
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)

enter image description here

问题

尽管我已经在对数空间中配置了插值,但根据提供的关系进行验证时,插值值仍然与计算值不匹配。我在下图中说明了这种差异,其中我通过将原始关系应用于插值结果来计算差异。

plt.figure(figsize=(18, 15))
plt.subplot(3, 1, 1)
plt.plot(capacities['hour_rates'], capacities['diff_cap'], label='Diff Capacity')
plt.plot(capacities['hour_rates'], capacities['diff_hr'], label='Diff Hour Rate')
plt.ylabel('Diff Interpolated / Calculated')
plt.xlabel('Hour Rate (h)')
plt.title('Interpolation Data Relationship By Hour Rate')
plt.legend()
max_tick = capacities['hour_rates'].max() + 10
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)
plt.subplot(3, 1, 2)
plt.plot(capacities['capacities'], capacities['diff_cap'], label='Diff Capacity')
plt.plot(capacities['capacities'], capacities['diff_hr'], label='Diff Hour Rate')
plt.ylabel('Diff Interpolated / Calculated')
plt.xlabel('Capacity (A/h)')
plt.title('Interpolation Data Relationship By Capacity')
plt.legend()
max_tick = capacities['capacities'].max() + 10
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)
plt.subplot(3, 1, 3)
plt.plot(capacities['currents'], capacities['diff_cap'], label='Diff Capacity')
plt.plot(capacities['currents'], capacities['diff_hr'], label='Diff Hour Rate')
plt.ylabel('Diff Interpolated / Calculated')
plt.xlabel('Current (A)')
plt.title('Interpolation Data Relationship By Current')
plt.legend()
max_tick = capacities['currents'].max() + 10
plot_grid(
    major_ticks=np.arange(0, max_tick, 20),
    minor_ticks=np.arange(0, max_tick, 5)
)

enter image description here

有没有办法提高这种类型的数据关系的对数尺度插值的准确性?据我了解,超出(4.86 A、135 A)范围的电流值可能会因外推而导致结果不准确。

python pandas numpy scipy interpolation
1个回答
0
投票

查看您的货币数据描述的关系:

hour_rates (h) = capacities (Ah) / currents (A)
capacities (Ah) = hour_rates (h) * currents (A)
currents (A) = capacities (Ah) / hour_rates (h)

您提供的数据中没有明确满足这些要求。我创建的数据与所呈现的结果完全相同:

capacity_data_corr = capacity[['hour_rates', 'capacities']]
capacity_data_corr['currents'] = capacity_data_corr['capacities']/capacity_data_corr['hour_rates']

插值几乎是理想的

enter image description here enter image description here

这意味着,获得的插值可能很好,但数据不满足假设的关系。如果这些关系只是近似的,那么在如此长的时间范围内,像这样的错误应该不会像看起来那么糟糕。

© www.soinside.com 2019 - 2024. All rights reserved.