In the last couple of articles, we built a solid foundation for integrating C# with Python using Python.NET. We explored how to pass complex data types, work with collections, and handle exceptions gracefully.
Now is the time to put all of this into practice and implement these concepts in a real-world application. In this article, we will work on a project that uses Python's pandas library to read and analyse data from a CSV file and return a clean and structured object to a C# application. This also gives your insights into the classic use case of Python.NET that allows you to handle the main application logic using C# and use Python's rich ecosystem to handle specialized tasks.
Setting Up the Project
To get started, create a new .NET console application and install the Python.NET package. You can do this from the command line:
dotnet new console -o PythonNetPandas
cd PythonNetPandas
dotnet add package pythonnet
Installing Python Dependencies
To analyze the data in our CSV file, your Python scripts will require the pandas library. It must be installed in the exact Python environment your C# application is using. In your command prompt, run the following command to add the pandas library to your Python interpreter:
[YOUR_PYTHON_INSTALLATION_PATH] -m pip install pandas
Note: Replace [YOUR_PYTHON_INSTALLATION_PATH]
with the actual location where your Python interpreter is installed. It should look something like this:
C:\Users\raoha\AppData\Local\Programs\Python\Python312\python.exe
Configuring the C# Project to Copy Files
This is a crucial step to avoid any FileNotFound
errors. It tells the C# compiler to automatically copy the Python script and data file into the application's output directory.
Open your PythonNetPandas.csproj
file and add the following <ItemGroup>
tag inside the main <Project>
label:
<ItemGroup>
<Content Include="data_analyzer.py">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="data.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
Save the file.
Creating the Data File
Inside your PythonNetPandas
directory, create a new CSV file named data.csv
and add the following contents to it:
product,sales,region
A,150,North
B,200,South
A,100,North
C,75,East
B,120,South
Creating the Python Module
Now, create a new Python file named data_analyzer.py
inside your project directory. Add the following code to it:
# data_analyzer.py
import pandas as pd
import json
def analyze_sales(csv_path):
"""
Reads a CSV, analyzes product sales using pandas, and returns a dictionary.
"""
print(f"Python: Reading data from {csv_path}...")
try:
# Read the CSV file into a pandas DataFrame.
df = pd.read_csv(csv_path)
# Group by product and sum the sales.
product_sales = df.groupby('product')['sales'].sum()
# Convert the pandas Series to a standard Python dictionary.
results_dict = product_sales.to_dict()
print("Python: Analysis complete. Returning results.")
return results_dict
except FileNotFoundError:
print(f"Error: The file at {csv_path} was not found.")
return None
except Exception as e:
print(f"An unexpected error occurred in Python: {e}")
return None
Writing the C# Integration Code
Now, as the final step, we will edit the Program.cs
file and integrate our Python module into our .NET application. Simply, launch the Program.cs
file in a code editor and add the following code to it:
// Program.cs
using System;
using System.IO;
using Python.Runtime;
class Program
{
static void Main(string[] args)
{
// Set the Python DLL. This is crucial for Python.NET to find the correct
// Python installation. Replace the path with the location of your pythonXX.dll.
Runtime.PythonDLL = "YOUR_PYTHON_INSTALLATION_PATH";
// Initialize the Python engine.
PythonEngine.Initialize();
try
{
using (Py.GIL())
{
// Set the path to our Python module and data file.
string appDirectory = AppDomain.CurrentDomain.BaseDirectory;
string pythonModulePath = Path.Combine(appDirectory, "data_analyzer.py");
string dataPath = Path.Combine(appDirectory, "data.csv");
// Get a reference to Python's sys module and add our module's directory to the search path.
dynamic sys = Py.Import("sys");
// Use a robust method to call the `insert` function on the Python list.
PyObject pyPath = sys.path;
pyPath.GetAttr("insert").Invoke(new PyInt(0), new PyString(Path.GetDirectoryName(pythonModulePath)));
// Import the Python module.
dynamic dataAnalyzer = Py.Import("data_analyzer");
Console.WriteLine("\n--- Calling Python to Analyze Data ---");
// Call the Python function and pass the data file path.
dynamic resultsDict = dataAnalyzer.analyze_sales(dataPath);
// Python.NET automatically converts the Python dictionary to a C# dynamic type.
if (resultsDict != null)
{
Console.WriteLine("\n--- C# Received the Analysis Results ---");
// We can now access the Python dictionary values directly.
foreach (dynamic key in resultsDict.keys())
{
dynamic value = resultsDict[key];
Console.WriteLine($"Product {key}: {value} sales");
}
}
}
}
finally
{
// Add a try-catch block to handle the shutdown exception in modern .NET.
try
{
PythonEngine.Shutdown();
}
catch (System.Exception ex)
{
Console.WriteLine($"An exception occurred during PythonEngine.Shutdown(): {ex.Message}");
// This exception is harmless and can be ignored to allow the application to exit gracefully.
}
}
}
}
Note: Before running the above code, ensure to replace "YOUR_PYTHON_INSTALLATION_PATH"
with the actual location where your Python interpreter is installed. It should look something like:
@"C:\\Users\\raoha\\AppData\\Local\\Programs\\Python\\Python312\\python312.dll"
Running the Application
We are all set to try to run our application. You should get the data in your CSV file in a well-structured format. To run the project, run the following command in your terminal:
dotnet run
You should expect the following output:
---Calling Python to Analyze Data ---
Python: Reading data from C:\PythonNetPandas\bin\Debug\net8.0\data.csv...
Python: Analysis complete. Returning results.
--- C# Received the Analysis Results ---
Product A: 250 sales
Product B: 320 sales
Product C: 75 sales
Bingo! You have explored the power of both Python and C# for data analysis applications. By combining both, you can build highly robust and powerful applications that use Python's vast library ecosystem to handle the complex tasks and integrate them into your .NET projects seamlessly.
In the last article of the series, we will take a look at more advanced topics like multithreading and GIL (Global Interpreter Lock), and explore some packaging and deployment practices.