Skip to main content

Extract data from DataVolley .dvw files and VolleyStation .vsm files

Project description

Avatar py-datavolley

A Python package for parsing and analyzing volleyball scouting data from DataVolley files (*.dvw).

Rebuilt pydatavolley with modern Python tooling (Astral ecosystem) for improved experience: UV for package management, Ruff for linting/formatting and Ty for type checking.

Quick Reference

# Start a new project
uv init my-analysis && cd my-analysis
uv add openvolley-pydatavolley[plot]

# Load and analyze data
uv run python -c "
import datavolley as dv
data = dv.read_dv('match.dvw')
print(f'Loaded {len(data)} plays')
"

Table of Contents

Prerequisites

This package works best with uv - a fast Python package manager.

Install uv

# macOS and Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

See uv documentation for more details.

Usage

Quick Start

If you have uv installed (recommended):

# Create a new project
uv init my-volleyball-analysis
cd my-volleyball-analysis

# Add the package
uv add openvolley-pydatavolley

# Plotting: Add plotting dependencies
uv add openvolley-pydatavolley[plot]

Create main.py:

import datavolley as dv

# Load a match
data = dv.read_dv('path/to/match.dvw')
# data = dv.read_dv(dv.example_file())

# Access play data
for play in data[:5]:
    print(f"{play['skill']}: {play['player_name']} - {play['evaluation_code']}")

# Filter by skill
serves = [p for p in data if p.get('skill') == 'Serve']
attacks = [p for p in data if p.get('skill') == 'Attack']

print(f"Total serves: {len(serves)}")
print(f"Total attacks: {len(attacks)}")

Run your analysis:

uv run main.py

With Pandas or Polars

Convert to DataFrame for easier analysis:

import datavolley as dv
import pandas as pd

data = dv.read_dv('match.dvw')
df = pd.DataFrame(data)

# Analyze attacks
attacks = df[df['skill'] == 'Attack']
print(attacks.groupby('player_name')['evaluation_code'].value_counts())

Or with Polars:

import datavolley as dv
import polars as pl

data = dv.read_dv('match.dvw')
df = pl.DataFrame(data)

# Fast filtering and aggregation
attacks = df.filter(pl.col('skill') == 'Attack')
print(attacks.group_by('player_name').agg(pl.len()))

Without uv

If you prefer pip:

pip install openvolley-pydatavolley
pip install openvolley-pydatavolley[plot]  # With plotting

Development

Want to contribute or modify the package? Here's how to set up a development environment:

  1. Clone the repository:

    git clone https://github.com/openvolley/py-datavolley.git
    cd py-datavolley
    
  2. Install dependencies:

    # UV automatically creates and manages virtual environments
    uv sync
    
  3. Run the example:

    uv run main.py
    
  4. Run linting and formatting:

    ruff check datavolley/
    ruff format datavolley/
    ty check datavolley/
    

Example Output

Running uv run main.py with the included example file will output:

Sample Play Data
[
  {
    "match_id": "106859",
    "video_time": 495,
    "code": "a02RM-~~~58AM~~00B",
    "team": "University of Dayton",
    "player_number": 2,
    "player_name": "Maura Collins",
    "player_id": "-230138",
    "skill": "Reception",
    "skill_type": "Jump-float serve reception",
    "skill_subtype": "Jump Float",
    "evaluation_code": "-",
    "setter_position": "6",
    "attack_code": null,
    "set_code": null,
    "set_type": null,
    "start_zone": "5",
    "end_zone": "8",
    "end_subzone": "A",
    "num_players_numeric": null,
    "home_team_score": "0",
    "visiting_team_score": "0",
    "home_setter_position": "1",
    "visiting_setter_position": "6",
    "custom_code": "00B",
    "home_p1": "19",
    "home_p2": "9",
    "home_p3": "11",
    "home_p4": "15",
    "home_p5": "10",
    "home_p6": "7",
    "visiting_p1": "1",
    "visiting_p2": "16",
    "visiting_p3": "17",
    "visiting_p4": "10",
    "visiting_p5": "6",
    "visiting_p6": "8",
    "start_coordinate": "0431",
    "mid_coordinate": "-1-1",
    "end_coordinate": "7642",
    "point_phase": "Reception",
    "attack_phase": null,
    "start_coordinate_x": 1.26875,
    "start_coordinate_y": 0.092596,
    "mid_coordinate_x": null,
    "mid_coordinate_y": null,
    "end_coordinate_x": 1.68125,
    "end_coordinate_y": 5.425924,
    "set_number": "1",
    "home_team": "University of Louisville",
    "visiting_team": "University of Dayton",
    "home_team_id": 17,
    "visiting_team_id": 42,
    "point_won_by": "University of Louisville",
    "serving_team": "University of Louisville",
    "receiving_team": "University of Dayton",
    "rally_number": 1,
    "possession_number": 1
  }
]

Plotting

The package includes plotting capabilities for visualizing volleyball match data on court diagrams.

Installation

Install with plotting dependencies:

uv add openvolley-pydatavolley[plot]

Or install dependencies separately:

uv add matplotlib numpy shapely

Examples

Draw a Court

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import datavolley as dv

# Draw a basic court
ax = dv.dv_court(zones=True, zone_labels=True)
plt.savefig('court.png')

Create Attack Heatmaps

import datavolley as dv

# Load match data
data = dv.read_dv('match.dvw')

# Get attack landing coordinates
attacks = [p for p in data if p.get('skill') == 'Attack']
coords = [(p['end_coordinate_x'], p['end_coordinate_y'])
          for p in attacks if p.get('end_coordinate_x')]

# Create heatmap
ax = dv.dv_heatmap(coords, zones=True)
plt.title(f'Attack Landing Zones (n={len(coords)})')
plt.savefig('attack_heatmap.png')

Plot Attack Trajectories

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import datavolley as dv

data = dv.read_dv(dv.example_file())

# Filter X5 attacks
attacks = [p for p in data
           if p.get('skill') == 'Attack' and p.get('attack_code') == 'X5']

# Plot trajectories
fig, ax = plt.subplots(figsize=(6, 10))
dv.dv_court(zones=True, ax=ax)

for attack in attacks:
    if all([attack.get('start_coordinate_x'), attack.get('end_coordinate_x')]):
        # Plot start (red) and end (blue) points
        ax.scatter(attack['start_coordinate_x'], attack['start_coordinate_y'],
                   color='red', s=50, alpha=0.6)
        ax.scatter(attack['end_coordinate_x'], attack['end_coordinate_y'],
                   color='blue', s=50, alpha=0.6)
        # Draw trajectory line
        ax.plot([attack['start_coordinate_x'], attack['end_coordinate_x']],
                [attack['start_coordinate_y'], attack['end_coordinate_y']],
                color='gray', linestyle='--', linewidth=1, alpha=0.5)

ax.set_title('X5 Attack Trajectories')
plt.savefig('x5_attacks.png', dpi=150)

Example Outputs

X5 Attack Trajectories Attack Comparison
X5 Attack Trajectories
Shows ball path from set to landing
Attack Code Comparison
Compare patterns across attack types

More examples available in the examples/ directory:

  • attack_analysis_complete.py - Complete attack trajectory analysis
  • attack_comparison.py - Compare multiple attack codes side-by-side zones
  • attack_heatmap.py - Heatmaps for attack set locations and landing
  • team_attacks.py - Compare attack patterns between teams

See examples/README.md for detailed documentation.

Contributing

Please create an issue, fork and create a pull request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openvolley_pydatavolley-0.2.0.tar.gz (459.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openvolley_pydatavolley-0.2.0-py3-none-any.whl (54.9 kB view details)

Uploaded Python 3

File details

Details for the file openvolley_pydatavolley-0.2.0.tar.gz.

File metadata

File hashes

Hashes for openvolley_pydatavolley-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3143f750e8ae95a07427add66e4de01b2ad16d95687e036f4f96b1864ac2d746
MD5 b4f0fbe4582bf1cb197f98ad7ad6ee4c
BLAKE2b-256 543a9faadee877bd65e6e242e69f7d12990bf5140681b0c54eb0505b75ee170a

See more details on using hashes here.

File details

Details for the file openvolley_pydatavolley-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for openvolley_pydatavolley-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0bf54b033b0af7b5fa01c0774824603e3da1a55f03185415677dfc255511c097
MD5 c71e4689ccfcd3056a9d2db0d0ed7049
BLAKE2b-256 a66903c6de78ceddecfb2132dc61ce62562cb67fd4803631f2fb4266f3d91485

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page