Skip to main content

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

Project description

py-datavolley

TLDR;

uv add openvolley-pydatavolley

# data = dv.read_dv(path_of_dvw_file)
data = dv.read_dv(dv.example_file())
print(data)
Will return (this is a sample and not the entire example file)
[
  {
    "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
  }
]


py-datavolley package

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.1.tar.gz (459.8 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.1-py3-none-any.whl (54.9 kB view details)

Uploaded Python 3

File details

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

File metadata

File hashes

Hashes for openvolley_pydatavolley-0.2.1.tar.gz
Algorithm Hash digest
SHA256 0ecfbf9eba340a9fd32611e87aa7c644611c59d14dba531086388509b3671af4
MD5 31aa9639d3394ff733f32aa6a5af9d3c
BLAKE2b-256 379a3fc13012e422f941af8a4fc4bc3c01a2596a728e0b79c927cca039b650ef

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for openvolley_pydatavolley-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8cdb7001d7bd8b1f2bb8a0e7a97b51e6a1130d2a4e2125310c9eccf3dcc11c17
MD5 ee842636717577aa3fca869ae4da4094
BLAKE2b-256 f7d78ff84336aa786240e7ece4af579f2d1cfdeaa60477531e9b646a0ad93d9c

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