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