C# pythonnet(2)_FFT傅立葉變換

Karl_Albright發表於2024-06-24

Python程式碼如下

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

# 讀取資料
data = pd.read_csv('clean_data_row.csv')
# 進行傅立葉變換
fft_result = np.fft.fft(data) 
frequencies = np.fft.fftfreq(len(data))

# 計算功率譜密度
power_spectrum = np.abs(fft_result)**2 / len(data)
print(len(power_spectrum))
frequencies_positive = frequencies[:len(frequencies)//2]

# 繪製頻譜圖和功率譜密度圖
# 頻譜圖
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.plot(frequencies, np.abs(fft_result))
plt.xlabel('Frequency (Hz)')
plt.ylabel('Amplitude')
plt.title('Frequency Spectrum')
# 功率譜密度圖
plt.subplot(1, 2, 2)
plt.plot(frequencies_positive, power_spectrum[:len(power_spectrum)//2])
plt.xlabel('Frequency (Hz)')
plt.ylabel('Power')
plt.title('Power Spectrum Density')

plt.tight_layout()
plt.show()

下面我們修改成C#程式碼

建立控制檯程式,Nuget安裝 CsvHelper 和 pythonnet

public class Program
{
    const string PathToPythonDir = "D:\\Python311";
    const string DllOfPython = "python311.dll";

    static void Main(string[] args)
    {
        // 傅立葉變換
        FFT();
    }
    /// <summary>
    /// 傅立葉變換
    /// </summary>
    static void FFT()
    {
        try
        {
            Runtime.PythonDLL = Path.Combine(PathToPythonDir, DllOfPython);
            PythonEngine.Initialize();
            using (Py.GIL())
            {
                dynamic pd = Py.Import("pandas");
                dynamic np = Py.Import("numpy");
                dynamic plt = Py.Import("matplotlib.pyplot");
                dynamic fft = Py.Import("scipy.fftpack");

                using dynamic scope = Py.CreateScope();
                scope.Exec(@"def get_slice(net_array): return net_array[:len(net_array)//2]");

                // 讀取資料
                var data = pd.read_csv("clean_data_row.csv");
                int listLength = data.__len__();
                Console.WriteLine("讀取長度:" + listLength);

                // 進行傅立葉變換
                var fft_result = fft.fft(data); // 對資料進行傅立葉變換
                var frequencies = fft.fftfreq(listLength);

                // 計算功率譜密度
                var power_spectrum = np.square(np.abs(fft_result)) / listLength;
                var frequencies_positive = scope.get_slice(frequencies);

                /*
                // 如果是api介面,直接返回x軸和y軸資料
                double[] xAxis = frequencies.As<double[]>();
                PyObject yAxisDatas = np.abs(fft_result);
                double[][] yAxis = yAxisDatas.As<dynamic[]>()
                    .Select(s => (double[])s.As<double[]>())
                    .ToArray();

                double[] xAxis2 = xAxis.Take(listLength / 2).ToArray();
                PyObject yAxisDatas2 = power_spectrum;
                double[][] yAxis2 = yAxisDatas2.As<dynamic[]>()
                    .Select(s => (double[])s.As<double[]>())
                    .Take(listLength / 2)
                    .ToArray();
                */

                // 繪製頻譜圖和功率譜密度圖
                plt.figure(figsize: new dynamic[] { 12, 6 });

                // 頻譜圖
                plt.subplot(1, 2, 1);
                plt.plot(frequencies, np.abs(fft_result));
                plt.xlabel("Frequency (Hz)");
                plt.ylabel("Amplitude");
                plt.title("Frequency Spectrum");

                // 功率譜密度圖
                plt.subplot(1, 2, 2);
                plt.plot(frequencies_positive, scope.get_slice(power_spectrum));
                plt.xlabel("Frequency (Hz)");
                plt.ylabel("Power");
                plt.title("Power Spectrum Density");

                // 佈局調整,防止重疊
                plt.tight_layout();
                // 顯示圖表
                plt.show();
            }
        }
        catch (Exception e)
        {
            Console.WriteLine("報錯了:" + e.Message + "\r\n" + e.StackTrace);
        }
    }

    /// <summary>
    /// 讀取CSV資料
    /// </summary>
    /// <param name="filePath">檔案路徑</param>
    /// <returns>檔案中資料集合,都是double型別</returns>
    static List<double[]> ReadCsvWithCsvHelper(string filePath)
    {
        using (var reader = new StreamReader(filePath))
        using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
        {
            var result = new List<double[]>();
            // 如果你的CSV檔案有標題行,可以呼叫ReadHeader來讀取它們
            csv.Read();
            csv.ReadHeader();
            while (csv.Read())
            {
                result.Add(new double[] {
                csv.GetField<double>(0),
                csv.GetField<double>(1),
                csv.GetField<double>(2),
            });
            }
            return result;
        }
    }
}

以下是執行後結果,

原始碼:https://gitee.com/Karl_Albright/csharp-demo/tree/master/PythonnetDemo/PythonnetFFT

這裡有人會問,為什麼不用 MathNet.Numerics 直接計算,因為計算結果和Python的結果差別太大了,希望有知道為什麼的大佬留言,這裡我也記錄以下實現步驟

建立Windows 窗體應用(WinForm),Nuget安裝 CsvHelper、MathNet.Numerics、OxyPlot.Core、OxyPlot.WindowsForms

public partial class Form1 : Form
{
    double[] xAxis = new double[0];
    double[][] yAxis = new double[0][];
    double[] xAxis2 = new double[0];
    double[][] yAxis2 = new double[0][];
    public Form1()
    {
        InitializeComponent();

        var datas = ReadCsvWithCsvHelper("clean_data_row.csv");
        CalcFFT(datas);
        DrawPlot();
    }
    OxyColor[] colors =
    [
        OxyColors.Blue,
        OxyColors.Yellow,
        OxyColors.Red,
        OxyColors.Green,
        OxyColors.Pink,
        OxyColors.Black,
        OxyColors.Orange,
    ];
    public List<double[]> ReadCsvWithCsvHelper(string filePath)
    {
        using (var reader = new StreamReader(filePath))
        using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
        {
            var result = new List<double[]>();
            // 如果你的CSV檔案有標題行,可以呼叫ReadHeader來讀取它們
            csv.Read();
            csv.ReadHeader();
            while (csv.Read()) 
            {
                result.Add([
                    csv.GetField<double>(0), 
                    csv.GetField<double>(1), 
                    csv.GetField<double>(2),
                ]);
            }
            return result;
        }
    }

    public void CalcFFT(List<double[]> datas)
    {
        var first = datas.First();
        yAxis = new double[first.Length][];
        yAxis2 = new double[first.Length][];
        for (int i = 0; i < first.Length; i++)
        {
            // 將資料轉換為Complex32陣列以便進行傅立葉變換
            Complex32[] dataComplex = datas.Select(item => new Complex32((float)item[i], 0)).ToArray();
            
            // 進行傅立葉變換
            Fourier.Forward(dataComplex, FourierOptions.AsymmetricScaling);

            var len = dataComplex.Length;
            // 計算頻率
            double[] frequencies = Fourier.FrequencyScale(len, 1);

            xAxis = frequencies;
            yAxis[i] = dataComplex.Select(x => Math.Abs(Math.Round(x.Magnitude, 7))).ToArray();

            xAxis2 = frequencies.Take(len / 2).ToArray();
            yAxis2[i] = dataComplex.Select(x => Math.Abs(Math.Round((x.Magnitude * x.Magnitude / len), 7))).Take(len / 2).ToArray();
        }

    }

    public void DrawPlot() 
    {
        // 繪製頻譜圖和功率譜密度圖(這裡使用OxyPlot庫)
        var plotModel = new PlotModel { Title = "Spectrum Analysis" };

        // 頻譜圖
        int xAxisLength = xAxis.Length;
        int yAxisLength = yAxis.Length;

        for (int i = 0; i < yAxisLength; i++)
        {
            var frequencySeries = new LineSeries
            {
                Title = "Item" + (i + 1),
                MarkerType = MarkerType.None,
                Color = colors[i]
            };
            for (int j = 0; j < xAxisLength; j++)
            {
                frequencySeries.Points.Add(new DataPoint(xAxis[j], yAxis[i][j]));
            }
            plotModel.Series.Add(frequencySeries);
        }
        plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Frequency (Hz)" });
        plotModel.Axes.Add(new LinearAxis { Position = AxisPosition.Left, Title = "Amplitude" });
        this.plotView1.Model = plotModel;

        var plotModel2 = new PlotModel { Title = "Power Spectrum Density" };
// 功率譜密度圖 int xAxis2Length = xAxis2.Length; int yAxis2Length = yAxis2.Length; for (int i = 0; i < yAxis2Length; i++) { var powerSeries = new LineSeries { Title = "Item" + (i + 1), MarkerType = MarkerType.None, Color = colors[i] }; for (int j = 0; j < xAxis2Length; j++) { powerSeries.Points.Add(new DataPoint(xAxis2[j], yAxis2[i][j])); } plotModel2.Series.Add(powerSeries); } plotModel2.Axes.Add(new LinearAxis { Position = AxisPosition.Bottom, Title = "Frequency (Hz)" }); plotModel2.Axes.Add(new LinearAxis { Position = AxisPosition.Right, Title = "Power" }); this.plotView2.Model = plotModel2; } }

原始碼:https://gitee.com/Karl_Albright/csharp-demo/tree/master/PythonnetDemo/PythonnetFFTWinFormsApp

相關文章